马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
ThreadLocal详解与高频场景实战指南
1. ThreadLocal概述
ThreadLocal是Java提供的线程当地变量机制,用于实现线程级别的数据隔离。每个访问该变量的线程都会得到独立的变量副本,实用于必要制止线程间共享数据的场景。
特点:
- 线程封闭性:数据仅对当火线程可见
- 无锁利用:天然线程安全
- 空间换时间:通过增长存储提拔性能
2. 焦点实现原理
- public class ThreadLocal<T> {
- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- // 每个线程拥有独立的ThreadLocalMap实例
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null) {
- return (T)e.value;
- }
- }
- return setInitialValue();
- }
- }
复制代码 数据布局:
- 每个Thread维护一个ThreadLocalMap
- Key为ThreadLocal实例(弱引用),Value为存储的值
- 办理哈希辩说:开放地点法
3. 高频利用场景与实战案例
3.1 用户会话管理
场景需求:在Web哀求处理处罚链中通报用户身份信息- public class UserContext {
- private static ThreadLocal<User> userHolder = new ThreadLocal<>();
-
- public static void setUser(User user) {
- userHolder.set(user);
- }
-
- public static User getUser() {
- return userHolder.get();
- }
-
- public static void clear() {
- userHolder.remove(); // 必须清理防止内存泄漏
- }
- }
- // 拦截器中设置用户信息
- public class AuthInterceptor implements HandlerInterceptor {
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse res, Object o) {
- User user = authService.verify(request.getHeader("token"));
- UserContext.setUser(user); // 存入ThreadLocal
- return true;
- }
- @Override
- public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object o, Exception e) {
- UserContext.clear(); // 请求结束清理
- }
- }
- // Service层直接获取
- @Service
- public class OrderService {
- public void createOrder() {
- User user = UserContext.getUser(); // 无需参数传递
- System.out.println("创建订单,用户:" + user.getId());
- }
- }
复制代码 3.2 数据库毗连管理
场景需求:包管同一事件中利用的数据库毗连划一- public class ConnectionManager {
- private static ThreadLocal<Connection> connHolder = ThreadLocal.withInitial(() -> {
- try {
- return DriverManager.getConnection("jdbc:mysql://localhost:3306/test");
- } catch (SQLException e) {
- throw new RuntimeException("获取连接失败", e);
- }
- });
- public static Connection getConn() {
- return connHolder.get();
- }
- public static void close() {
- Connection conn = connHolder.get();
- if (conn != null) {
- try {
- conn.close();
- } catch (SQLException ignored) {}
- connHolder.remove(); // 关键!
- }
- }
- }
- // 使用示例
- public void executeQuery() {
- try {
- Connection conn = ConnectionManager.getConn();
- Statement stmt = conn.createStatement();
- ResultSet rs = stmt.executeQuery("SELECT * FROM user");
- // 处理结果...
- } finally {
- ConnectionManager.close(); // 确保关闭并清理
- }
- }
复制代码 3.3 线程安整日期格式化
场景需求:SimpleDateFormat非线程安全,同步利用性能低。- public class DateUtils {
- private static ThreadLocal<SimpleDateFormat> sdfHolder = ThreadLocal.withInitial(
- () -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
- );
- public static String format(Date date) {
- return sdfHolder.get().format(date);
- }
- }
- // 多线程并发调用安全
- ExecutorService pool = Executors.newFixedThreadPool(10);
- for (int i = 0; i < 100; i++) {
- pool.execute(() -> {
- String dateStr = DateUtils.format(new Date());
- System.out.println(dateStr);
- });
- }
复制代码 3.4 事件上下文通报
场景需求:在多层方法调用中通报事件状态- public class TransactionContext {
- private static ThreadLocal<Boolean> transactionActive =
- ThreadLocal.withInitial(() -> false);
- public static void begin() {
- transactionActive.set(true);
- }
- public static boolean isActive() {
- return transactionActive.get();
- }
- public static void end() {
- transactionActive.remove();
- }
- }
- // 使用AOP管理事务
- @Aspect
- public class TransactionAspect {
- @Around("@annotation(transactional)")
- public Object manageTransaction(ProceedingJoinPoint pjp) throws Throwable {
- try {
- TransactionContext.begin();
- Object result = pjp.proceed();
- TransactionContext.end();
- return result;
- } catch (Exception e) {
- TransactionContext.end();
- throw e;
- }
- }
- }
复制代码 3.5、全局TraceID通报(全链路追踪)
需求:为每个哀求天生唯一TraceID,贯穿日志 打印、RPC调用等环节。- public class TraceContext {
- private static ThreadLocal<String> traceIdHolder = new ThreadLocal<>();
- public static void startTrace() {
- traceIdHolder.set(UUID.randomUUID().toString());
- }
- public static String getTraceId() {
- return traceIdHolder.get();
- }
- public static void endTrace() {
- traceIdHolder.remove();
- }
- }
- // 日志
切面增强 - @Aspect
- @Component
- public class LogAspect {
- @Around("execution(* com.example.service.*.*(..))")
- public Object around(ProceedingJoinPoint pjp) throws Throwable {
- MDC.put("traceId", TraceContext.getTraceId()); // 日志
框架集成 - try {
- return pjp.proceed();
- } finally {
- MDC.clear();
- TraceContext.endTrace();
- }
- }
- }
复制代码 4. 注意事项与内存走漏
内存走漏风险
- 根本缘故起因:ThreadLocalMap.Entry的key是弱引用,value是强引用
- 办理方案:
- 每次利用后必须调用remove()
- 利用static final修饰ThreadLocal实例
- 制止在线程池环境长期持有
最佳实践
- try {
- threadLocal.set(value);
- // 业务逻辑...
- } finally {
- threadLocal.remove(); // 必须清理
- }
复制代码 5. 总结
实用场景:
- 必要在线程生命周期内通报上下文信息
- 高频创建昂贵对象(如数据库毗连)
- 必要线程隔离的全局变量
上风:
- 淘汰参数通抨击杂度
- 进步线程安全性
- 提拔资源复用服从
利用原则:
- 优先思量方法参数通报
- 仅在确实必要线程隔离时利用
- 始终遵照set-remove配对原则
|