[!IMPORTANT] 🚧 这个分支是
TransmittableThreadLocal(TTL) v3
,在开发中还没有发布。
v3
的版本说明、工作项列表及其进展,参见 issue 432。👉 目前使用中的稳定发布版本
v2.x
在 分支2.x
上。
📖 English Documentation | 📖 中文文档
- 🔧 功能
- 🎨 需求场景
- 👥 User Guide
- 🔌 Java API Docs
- 🍪 Maven依赖
- 🔨 关于编译构建
- ❓ FAQ
- ✨ 使用
TTL
的好处与必要性 - 🗿 更多文档
- 📚 相关资料
- 💗 Who Used
- 👷 Contributors
🔧 功能
👉 TransmittableThreadLocal
(TTL
):在使用线程池等会池化复用线程的执行组件情况下,提供ThreadLocal
值的传递功能,解决异步执行时上下文传递的问题。一个Java
标准库本应为框架/中间件设施开发提供的标配能力,本库功能聚焦 & 0依赖,支持Java 6~21
。
JDK
的InheritableThreadLocal
类可以完成父线程到子线程的值传递。但对于使用线程池等会池化复用线程的执行组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal
值传递已经没有意义,应用需要的实际上是把 任务提交给线程池时的ThreadLocal
值传递到 任务执行时。
本库提供的TransmittableThreadLocal
类继承并加强InheritableThreadLocal
类,解决上述的问题,使用详见 User Guide。
整个TransmittableThreadLocal
库的核心功能(用户API
、线程池ExecutorService
/ForkJoinPool
/TimerTask
及其线程工厂的Wrapper
;开发者API
、框架/中间件的集成API
),只有 ~1000 SLOC
代码行,非常精小。
欢迎 👏
- 建议和提问,提交 Issue
- 贡献和改进,Fork 后提通过 Pull Request 贡献代码
🎨 需求场景
ThreadLocal
的需求场景即TransmittableThreadLocal
的潜在需求场景,如果你的业务需要『在使用线程池等会池化复用线程的执行组件情况下传递ThreadLocal
值』则是TransmittableThreadLocal
目标场景。
下面是几个典型场景例子。
- 分布式跟踪系统 或 全链路压测(即链路打标)
- 日志收集记录系统上下文
Session
级Cache
- 应用容器或上层框架跨应用代码给下层
SDK
传递信息
各个场景的展开说明参见子文档 需求场景。
👥 User Guide
使用类TransmittableThreadLocal
来保存值,并跨线程池传递。
TransmittableThreadLocal
继承InheritableThreadLocal
,使用方式也类似。相比InheritableThreadLocal
,添加了protected
的transmitteeValue()
方法,用于定制 任务提交给线程池时 的ThreadLocal
值传递到 任务执行时 的传递方式,缺省是简单的赋值传递。
注意:如果传递的对象(引用类型)会被修改,且没有做深拷贝(如直接传递引用或是浅拷贝),那么
- 因为跨线程传递而不再有线程封闭,传递对象在多个线程之间是有共享的。
- 与
JDK
的InheritableThreadLocal.childValue()
一样,需要使用者/业务逻辑注意保证传递对象的线程安全。
关于
transmitteeValue
方法 的 展开说明
关于构词后缀
er
与ee
的说明:
transmit
是动词传递,transmitter
动作的执行者/主动方,而transmittee
动作的接收者/被动方。er
与ee
后缀的常见词是employer
(雇主)/employee
(雇员)、caller
(调用者)/callee
(被调用者)。
具体使用方式见下面的说明。
1. 简单使用
父线程给子线程传递值。
示例代码:
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();
// =====================================================
// 在父线程中设置
context.set("value-set-in-parent");
// =====================================================
// 在子线程中可以读取,值是"value-set-in-parent"
String value = context.get();
# 完整可运行的Demo代码参见SimpleDemo.kt
。
这其实是InheritableThreadLocal
的功能,应该使用InheritableThreadLocal
来完成。
但对于使用线程池等会池化复用线程的执行组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal
值传递已经没有意义,应用需要的实际上是把 任务提交给线程池时的ThreadLocal
值传递到 任务执行时。
解决方法参见下面的这几种用法。
2. 保证线程池中传递值
2.1 修饰Runnable
和Callable
使用TtlRunnable
和TtlCallable
来修饰传入线程池的Runnable
和Callable
。
示例代码:
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();
// =====================================================
// 在父线程中设置
context.set("value-set-in-parent");
Runnable task = new RunnableTask();
// 额外的处理,生成修饰了的对象ttlRunnable
Runnable ttlRunnable = TtlRunnable.get(task);
executorService.submit(ttlRunnable);
// =====================================================
// Task中可以读取,值是"value-set-in-parent"
String value = context.get();
注意: