在开发类似 Spring MVC 这种有多层模型、多线程时,会有个跨层数据共享的问题。比如在 controller 准备好了数据需要传递给 Model 或 View,又或者,我们再拦截器中准备好了数据需要给到其他层来使用,而且只给当前线程使用,当前线程消亡后,数据也要跟着消亡,我把这种的数据共享称为“上下文”。比如,我们在 012 Spring boot 上下文context 就应用到了。
1. ThreadLocal 是什么?
ThreadLocal 叫做本地线程变量,意思是说,ThreadLocal 中填充的的是当前线程的变量,该变量对其他线程而言是封闭且隔离的,ThreadLocal 为变量在每个线程中创建了一个副本,这样每个线程都可以访问自己内部的副本变量。
特点
- 在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
- 线程间数据隔离
- 进行事务操作,用于存储线程事务信息。
- 数据库连接,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 删除掉。