问题描述:
ThreadLocal可以用于存储线程独享的变量。可以方便的存储上下文信息,提升代码的简洁性。
然而,ThreadLocal的一个不足之处在于,它不支持在线程嵌套过程中自动地将数据从父线程传递到子线程。这意味着,即使主线程中设置了ThreadLocal变量,子线程默认情况下也无法访问这些变量。
一、引言
ThreadLocal
的需求场景即TransmittableThreadLocal
的潜在需求场景,如果你的业务需要『在使用线程池等会池化复用线程的执行组件情况下传递ThreadLocal
值』则是TransmittableThreadLocal
目标场景。
下面是几个典型场景例子。
- 分布式跟踪系统 或 全链路压测(即链路打标)
- 日志收集记录系统上下文
Session
级Cache
- 应用容器或上层框架跨应用代码给下层
SDK
传递信息
详细需求参见文章末尾参考链接
二、线程内数据共享
2.1 ThreadLocal
ThreadLocal可以用于存储线程独享的变量。很容易想到可以使用ThreadLocal用于我们的多语言信息存储。
然而,ThreadLocal的一个不足之处在于,它不支持在线程嵌套过程中自动地将数据从父线程传递到子线程。这意味着,即使主线程中设置了ThreadLocal变量,子线程默认情况下也无法访问这些变量。
demo
public class TlTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
ThreadLocal<String> threadLocal = new ThreadLocal<String>();
threadLocal.set("main thread set value");
executorService.submit(() -> {
String value = threadLocal.get();
System.out.printf("child threadA, value is : %s%n", value);
});
executorService.submit(() -> {
String value = threadLocal.get();
System.out.printf("child threadB, value is : %s%n", value);
});
Thread.sleep(1000);
System.out.printf("main thread, value is : %s%n", threadLocal.get());
}
}
运行结果:只有主线程中可以获取到数据,子线程无法获取到数据
2.2 InheritableThreadLocal
Java提供了InheritableThreadLocal,允许子线程继承并使用父线程中的线程本地变量。但是,当InheritableThreadLocal应用于线程池时,它可能会引发问题。线程池中的核心线程通常不会在每次任务完成后立即被回收,这意味着存储在InheritableThreadLocal中的数据也不会被清除。因此,当线程被复用后,执行后续任务时,新任务可能会读取到旧任务留下的脏数据。
demo:
public class ITlTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<String>();
threadLocal.set("main thread set value");
executorService.submit(() -> {
String value = threadLocal.get();
System.out.printf("child threadA, lang is : %s%n", value);
threadLocal.set("child threadA set Value");
});
executorService.submit(() -> {
String value = threadLocal.get();
System.out.printf("child threadB, lang is : %s%n", value);
threadLocal.set("child threadB set Value");
});
Thread.sleep(1000);
System.out.printf("main thread, value is : %s%n", threadLocal.get());
}
}
运行结果:子线程B获得到了子线程A中设置的脏数据。
2.3 TransmittableThreadLocal
TransmittableThreadLocal(TTL)是对TransmittableThreadLocal的一个改进,它解决了线程池等池化场景中的数据传递问题。通过提供一种机制来传递ThreadLocal值,TTL确保了在异步执行和上下文传递数据时的准确性。这使得线程池中的线程能够在执行任务时正确地接收和传递数据,避免了因数据不一致性而可能导致的问题。
demo:
public class TtdlTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService = TtlExecutors.getTtlExecutorService(executorService);
TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<String>();
threadLocal.set("main thread set value");
executorService.submit(() -> {
String value = threadLocal.get();
System.out.printf("child threadA, lang is : %s%n", value);
threadLocal.set("child threadA set Value");
});
executorService.submit(() -> {
String value = threadLocal.get();
System.out.printf("child threadB, lang is : %s%n", value);
threadLocal.set("child threadB set Value");
});
Thread.sleep(1000);
System.out.printf("main thread, value is : %s%n", threadLocal.get());
}
}
运行结果:主线程、子线程A、子线程B均可以正确的读取到主线程中设置的数据
三、结论
TransmittableThreadLocal提供了用于在多线程和异步执行场景中传递上下文数据的解决方案。通过使用TTDL,可以避免线程安全问题,减少线程池中的数据不一致问题,简化代码逻辑,提高应用程序的国际化和可维护性。
👉 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
代码行,非常精小。
参考资料
•TransmittableThreadLocal官方GitHub仓库:GitHub - alibaba/transmittable-thread-local: 📌 a missing Java std lib(simple & 0-dependency) for framework/middleware, provide an enhanced InheritableThreadLocal that transmits values between threads even using thread pooling components.