ThreadLocal

在开发类似 Spring MVC 这种有多层模型、多线程时,会有个跨层数据共享的问题。比如在 controller 准备好了数据需要传递给 Model 或 View,又或者,我们再拦截器中准备好了数据需要给到其他层来使用,而且只给当前线程使用,当前线程消亡后,数据也要跟着消亡,我把这种的数据共享称为“上下文”。比如,我们在 012 Spring boot 上下文context 就应用到了。

1. ThreadLocal 是什么?

ThreadLocal 叫做本地线程变量,意思是说,ThreadLocal 中填充的的是当前线程的变量,该变量对其他线程而言是封闭且隔离的,ThreadLocal 为变量在每个线程中创建了一个副本,这样每个线程都可以访问自己内部的副本变量。

特点

  1. 在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
  2. 线程间数据隔离
  3. 进行事务操作,用于存储线程事务信息。
  4. 数据库连接,Session会话管理。

2. ThreadLocal 用法

实际应用场景的用法,请参照 012 Spring boot 上下文context (内部包含源码)

3. 关于 ThreadLocal 内存泄露的问题

网上分析源码,有内存泄露的可能。目前我是在 Spring Boot 实际开发过程中用到了 ThreadLocal,通过设置断点跟踪和分析,发现每个线程在启动和结束时,都会调用 ThreadLocal.java :

/**
* Removes the current thread's value for this thread-local
* variable.  If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim.  This may result in multiple invocations of the
* {@code initialValue} method in the current thread.
*
* @since 1.5
*/
public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
    m.remove(this);
}

这个 remove() 方法,就是 ThreadLocal 提供的清除 thread-local variable,但是为安全起见,在实际开发过程中,我也手动进行删除操作:

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("==========afterCompletion================");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);

JDZBaseContextHolder.removeContext();
}

即,在 Spring Boot 一次请求结束后,把设置的 ThreadLocal 删除掉。

发表评论