前言
Java分布式系统中经常会用到 ThreadLocal 来解决线程间数据隔离性的问题,但是 ThreadLocal 存在父子线程间值无法传递的局限性,为了解决上面的问题 InheritableThreadLocal 就应运而生,然而 InheritableThreadLocal 又存在着池化子线程与父线程间值无法传递的局限性,为了解决上面这些问题,最终阿里的 TransmittableThreadLocal 脱颖而出,本文将重点以代码来演示TTL的一些用法。
一、TransmittableThreadLocal简介
在使用线程池等会池化复用线程的执行组件情况下,提供ThreadLocal值的传递功能,解决异步执行时上下文传递的问题。
JDK的InheritableThreadLocal类可以完成父线程到子线程的值传递。但对于使用线程池等会池化复用线程的执行组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal值传递已经没有意义,应用需要的实际上是把 任务提交给线程池时的ThreadLocal值传递到 任务执行时。
本库提供的TransmittableThreadLocal类继承并加强InheritableThreadLocal类,解决上述的问题,使用详见User Guide。
二、TransmittableThreadLocal用法
1. 简单使用
父线程给子线程传递值。
示例代码:
1 | TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>(); |
# 完整可运行的Demo代码参见SimpleDemo.kt。
这是其实是InheritableThreadLocal的功能,应该使用InheritableThreadLocal来完成。
但对于使用线程池等会池化复用线程的执行组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal值传递已经没有意义,应用需要的实际上是把 任务提交给线程池时 的ThreadLocal值传递到 任务执行时。
解决方法参见下面的这几种用法。
2. 保证线程池中传递值
2.1 修饰Runnable和Callable
使用TtlRunnable和TtlCallable来修饰传入线程池的Runnable和Callable。
示例代码:
1 | TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>(); |
上面演示了Runnable,Callable的处理类似
1 | TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>(); |
# 完整可运行的Demo代码参见TtlWrapperDemo.kt。
整个过程的完整时序图

2.2 修饰线程池
省去每次Runnable和Callable传入线程池时的修饰,这个逻辑可以在线程池中完成。
通过工具类com.alibaba.ttl.threadpool.TtlExecutors完成,有下面的方法:
getTtlExecutor:修饰接口ExecutorgetTtlExecutorService:修饰接口ExecutorServicegetTtlScheduledExecutorService:修饰接口ScheduledExecutorService
示例代码:
1 | ExecutorService executorService = ... |
# 完整可运行的Demo代码参见TtlExecutorWrapperDemo.kt。
2.3 使用Java Agent来修饰JDK线程池实现类
这种方式,实现线程池的传递是透明的,业务代码中没有修饰Runnable或是线程池的代码。即可以做到应用代码 无侵入。
# 关于 无侵入 的更多说明参见文档Java Agent方式对应用代码无侵入。
示例代码:
1 | // ## 1. 框架上层逻辑,后续流程框架调用业务 ## |
Demo参见AgentDemo.kt。执行工程下的脚本scripts/run-agent-demo.sh即可运行Demo。
三、TransmittableThreadLocal实践
测试类代码
1 | /** |
threadLocalStr运行结果:
1 | 09:13:09.290 [main] INFO com.chinacscs.mp.ThreadLocalTest - 创建ttl线程池! |
从执行结果可以看出 ThreadLocal 和 InheritableThreadLocal 都无法在池化线程子线程获取父线程的值
threadLocalMap运行结果:
1 | 09:14:19.330 [main] INFO com.chinacscs.mp.ThreadLocalTest - 创建ttl线程池! |
从执行结果可以看出,TTL默认创建的Map变量在父子线程会相互影响,两者用的是同一个Map,查看源码TTL线程默认的copy方法复制的是父线程Map的引用,所以父线程的改动会影响子线程的Map一起变化,重写copy方法新建一个Map后父线程和子线程就不会相互影响了
参考文章
1.ThreadLocal系列之——父子线程传递线程私有数据
2.ThreadLocal父子线程传递实现方案
3.阿里开源TransmittableThreadLocal使用经验记录
4.TransmittableThreadLocal源码分析
- 本文作者: yinshuang
- 本文链接: https://yinshuang007.github.io/2023/11/20/阿里TransmittableThreadLocal实践/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!