Java ThreadLocal 相关

Java ThreadLocal.

常见用法

ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

——ThreadLocal 的 Javdoc

ThreadLocal 的变量通常是 private static 类型的变量。

注意事项

《阿里巴巴 Java 开发手册》中对 ThreadLocal 的使用有如下规定:

【强制】必须回收自定义的 ThreadLocal 变量,尤其在线程池场景下,线程经常会被复用,如果不清理自定义的 ThreadLocal 变量,可能会影响后续业务逻辑和造成内存泄露等问题。尽量在代理中使用 try-finally 块进行回收。

正例:

1
2
3
4
5
6
7
> objectThreadLocal.set(userInfo);
> try {
> // ...
> } finally {
> objectThreadLocal.remove();
> }
>

【参考】ThreadLocal 对象使用 static 修饰,ThreadLocal 无法解决共享对象的更新问题。

说明:这个变量是针对一个线程内所有操作共享的,所以设置为静态变量,所有此类实例共享此静态变量,也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象(只要是这个线程内定义的)都可以操控这个变量。

ThreadLocal 中有一个内部类 ThreadLocalMap

可能的内存泄漏问题

其实 ThreadLocal 这个类已经考虑到了可能的内存泄漏问题。所以其内部使用到了弱引用。在好几个操作中也会对进行操作防止内存泄漏。

InheritableThreadLocal

ThreadLocal 的一个子类,用于子线程获取父线程的 ThreadLocal 变量。

【并发编程】InheritableThreadLocal使用详解 - 程序员自由之路 - 博客园

讲透 ThreadLocal 和 InheritableThreadLocal - 掘金

InheritableThreadLocal线程池下失效问题解决 - 知乎

当InheritableThreadLocal遇到线程池:主线程本地变量修改后,子线程无法读取到新值inheritedthreadlocal线程池快乐崇拜234的博客-CSDN博客

遇到线程池InheritableThreadLocal就废了,该怎么办? - 简书

alibaba/transmittable-thread-local: 📌 TransmittableThreadLocal (TTL), the missing Java™ std lib(simple & 0-dependency) for framework/middleware, provide an enhanced InheritableThreadLocal that transmits values between threads even using thread pooling components.

ThreadLocal垮线程池传递数据解决方案:TransmittableThreadLocal【享学Java】-腾讯云开发者社区-腾讯云

从ThreadLocal谈到TransmittableThreadLocal,从使用到原理 - 掘金

注意事项

slf4j MDCAdapter with multi-thread-context 支持 · Issue #51 · alibaba/transmittable-thread-local

initialValue() 的触发时机

调用 remove 后再调用 get,会再次调用 initialValue() 方法

TransmittableThreadLocal

注意事项

避免线程池创建线程时继承 TransmittableThreadLocal

1
2
3
4
5
6
/**
* TransmittableThreadLocal 是继承自 InheritableThreadLocal 的,所以如果持有 TransmittableThreadLocal
* 的线程初次调用线程池,可能会把值传递给线程池中的线程,导致内存泄漏且不会自动清除。比如管道任务跑全量阶段,管道会调用和定时共享的 TaskGroupPool
* 创建线程池的时候,使用 TtlExecutors.getDisableInheritableThreadFactory 避免把 TransmittableThreadLocal 传递给
* 线程池中的线程
*/

方案1:使用 TtlExecutors.getDisableInheritableThreadFactory,这个可以在创建线程前把当前线程的 TransmittableThreadLocal remove 掉,等创建完线程后再设置回来,从而避免继承。

其他:

Here are some thoughts about the inheritable feature:

case 1

Inheritable feature in thread pooling components(ThreadPoolExecutor etc.) should never happen, because threads in thread pooling components is pre-created and pooled, so these threads is neutral for biz logic/data.

case 2

Whether the value should be inheritable or not can be controlled by the data owner, disable it carefully when data owner have a clear idea.

disable inheritable by overriding the childValue and return initialValue():

1
2
3
4
5
6
> TransmittableThreadLocal<String> transmittableThreadLocal = new TransmittableThreadLocal<String>() {
> protected String childValue(String parentValue) {
> return initialValue();
> }
> }
>

>

——disable Inheritable when it’s not necessary and buggy(eg. has potential memory leaking problem) · Issue #100 · alibaba/transmittable-thread-local

TransmittableThreadLocal会不会有内存泄漏的风险? · Issue #281 · alibaba/transmittable-thread-local

注意事项

注意1:当父线程直接设置 ThreadLocal 为 null 的时候,子线程获取到的可能不是 null,而是 initialValue

对于 set(null) 保持和 InheritableThreadLocal 一致语义? · Issue #157 · alibaba/transmittable-thread-local

相关编码规范

  1. TPS04-J. Ensure ThreadLocal variables are reinitialized when using thread pools - SEI CERT Oracle Coding Standard for Java - Confluence

参考资料

  1. 使用ThreadLocal - 廖雪峰的官方网站
  2. MemoryLeakProtection - Apache Tomcat【自定义继承了 ThreadLocal 的类可能导致内存泄漏(已修复)】
  3. 使用ThreadLocal不当可能会导致内存泄露 | 并发编程网
  4. Java 理论与实践: 用弱引用堵住内存泄漏 - IBM Developer
  5. 【死磕 Java 并发】—– 深入分析 ThreadLocal - 芋道源码
  6. ThreadLocal 内存泄露的实例分析
  7. 深入分析 ThreadLocal 内存泄漏问题
  8. Java多线程父子线程关系 多线程中篇(六) - noteless - 博客园
  9. java - Is it really my job to clean up ThreadLocal resources when classes have been exposed to a thread pool? - Stack Overflow