上一个博客中提到的 InheritableThreadLocal在线程池的应用中会产生问题.
博客地址:https://blog.csdn.net/hewenbo111/article/details/80487252
因为线程池中的线程是会被复用的,所以在使用线程池进行多线程编程时,如需操作本地变量则需要特别注意!
本次博客会演示使用 TransmittableThreadLocal 解决线程本地变量在线程池之间的传递问题。
问题复现:
创建一个线程池并设置默认的大小为2,并在主线程中多次对线程本地变量赋值,每次赋值完以后线程池获取,如下:
运行结果: 主线程中对于本地变量的修改并没有完全引起线程池中的线程本地变量的修改。
问题分析:
InheritableThreadLocal可以实现线程本地变量在父子线程之间的传递,而线程池中的线程是复用的,所以要想分析出为什么能得到如上的执行结果,我们需要弄清楚线程池中的线程何时被创建。前面博客对线程池的源代码有过简单的剖析,这里只说结论。
线程池在使用的时候,例如上述代码中的submit操作,如果线程池内线程的数量小于开始设定的线程数量,则会把当前的任务作为第一个任务并创建新的线程去执行。所以上述代码中前两次使用线程池submit task的时候,变量分别传递成功。因为线程池的线程是主线程创建的子线程。而在submit task的时候,如果线程池中线程数量已经达到设定值,则不会创建线程,而是复用线程池中的线程,所以线程池中线程的本地变量也是复用的创建时设置的变量。因而出现后两次变量传递不成功的现象。
解决方案:
引入阿里提供的技术:TransmittableThreadLocal
github地址:https://github.com/alibaba/transmittable-thread-local
使用步骤:
①:引入依赖包到pom文件中
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.2.0</version>
</dependency>
②:修改代码使用新的线程本地变量
③: 根据官方给定的方式进行修饰,保证值的正确传递
3.1 修饰Runnable
和Callable
3.2 TtlExecutors 修饰线程池
3.3 使用Java Agent
来修饰JDK
线程池实现类
本人使用的是方法2,直接对线程池进行修饰,便于集中管理. 详情可参考github文档描述.
④:运行结果
具体的实现原理后续再阅读并整理,文章多有不足,欢迎大家指正,讨论。万分感谢!!!