[!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目标场景。
下面是几个典型场景例子。
- 分布式跟踪系统 或 全链路压测(即链路打标)
- 日志收集记录系统上下文
Request级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。
示例代码:
T
