TransmittableThreadLocal的实现机制和原理
1 前言
前面我看过了 ThreadLocal的实现机制和原理 以及 InheritableThreadLocal的实现机制和原理 两种类型的 ThreadLocal,前者是普通的,后者是在前者的基础上套了一层父子线程关系,当使用后者的时候,会在线程创建的时候,浅拷贝一份父线程的变量值。那么今天空了,我来看看另外一种 ThreadLocal:TransmittableThreadLocal。
2 TransmittableThreadLocal
2.1 TransmittableThreadLocal 的认识
TransmittableThreadLocal(TTL)是阿里巴巴开源的一个 Java 库,用于解决 ThreadLocal 在多线程环境下的一些问题,尤其是在使用线程池等场景下可能出现的问题。与普通的 ThreadLocal 不同,TTL 具有以下特点:
- 线程池透传性:在使用线程池执行任务时,TTL 可以透传 ThreadLocal 的值,确保后续线程能够正确访问前线程设置的 TransmittableThreadLocal 变量值。
- 线程池隔离性:TTL 在多线程环境下能够确保每个线程都有独立的 TransmittableThreadLocal值,避免了线程池重用线程时可能出现的数据污染问题,比如线程池执行前从父线程继承的变量,不管是执行中变没变,下次执行任务的时候,还是会和父线程保持一致。
- 资源自动清理:TTL 支持自动清理 TransmittableThreadLocal 值,避免了可能导致内存泄漏的问题。
- 兼容性:TTL 兼容原生 ThreadLocal 的语法和用法,可以直接替换原生 ThreadLocal 使用,而无需修改现有代码。
2.2 TransmittableThreadLocal 的使用
TTL 通常用于需要在线程池中执行任务,并且需要在任务之间传递 ThreadLocal 值的场景。例如,在 Web 应用中,可能需要在异步任务中访问当前用户的会话信息,而使用 TTL 可以确保子线程能够正确访问父线程设置的会话信息。TransmittableThreadLocal在使用线程池等会池化复用线程的执行组件情况下,提供ThreadLocal值的传递功能(把任务提交给线程池时的ThreadLocal值传递到任务执行时),解决异步执行时上下文传递的问题。我们来简单体验一下:
// TTL private static final TransmittableThreadLocal<Integer> tl = new TransmittableThreadLocal<>(); public static void main(String[] args) { // 父线程设置变量 1 tl.set(1); new Thread(() -> { System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get())); }).start(); new Thread(() -> { System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get())); }).start(); }
这么一看貌似跟我们的 ITL 没什么区别是吧。我们来看一个线程池的例子:
public class Demo { private static final TransmittableThreadLocal<Integer> tl = new TransmittableThreadLocal<>(); private static final ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor( 2, 2, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100) ); public static void main(String[] args) { // 父线程设置变量 1 tl.set(1);
poolExecutor.execute(() -> { // 更改当前线程中的值 tl.set(2); System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get())); });
poolExecutor.execute(() -> { System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get())); });
poolExecutor.execute(() -> { System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get())); });
poolExecutor.execute(() -> { System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get())); }); } }
我们看普通情况下,当某个线程改变了 TTL 的值后,下次该线程执行任务的时候,TTL 的值就是改变后的了。这里需要引入一下 TTL 里的线程池,我们再看:
public class Demo { private static final TransmittableThreadLocal<Integer> tl = new TransmittableThreadLocal<>(); private static final ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor( 2, 2, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100) ); private static final Executor ttlExecutor = TtlExecutors.getTtlExecutor(poolExecutor); public static void main(String[] args) { // 父线程设置变量 1 tl.set(1); ttlExecutor.execute(() -> { // 更改当前线程中的值 tl.set(2); System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get())); }); ttlExecutor.execute(() -> { System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get())); }); ttlExecutor.execute(() -> { System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get())); }); ttlExecutor.execute(() -> { System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get())); }); } }
2.4 应用场景
TransmittableThreadLocal用来实现线程间的参数传递,经典应用场景如下:
(1)分布式跟踪系统 或 全链路压测(即链路打标)
(2)日志收集记录系统上下文
(3)Session级 Cache
(4)应用容器或上层框架跨应用代码给下层 SDK传递信息
2.5 ThreadLocal、InheritableThreaLocal与TransmittableThreadLocal的比较
ThreadLocal、InheritableThreadLocal与TransmittableThreadLocal在Java中都是用于处理线程局部变量的工具,但它们在使用场景和特性上有所不同。
(1)ThreadLocal
ThreadLocal是Java中一个非常重要的线程技术,它为每个线程提供了它自己的变量副本,使得线程间无法相互访问对方的变量,从而避免了线程间的竞争和数据泄露问题。适用于需要在线程内部存储和获取数据,且不希望与其他线程共享数据的场景。
(2)InheritableThreadLocal
InheritableThreadLocal是ThreadLocal的一个子类,它包含了ThreadLocal的所有功能,并扩展了ThreadLocal的功能。 允许父线程中的InheritableThreadLocal变量的值被子线程继承。当创建一个新的线程时,这个新线程可以访问其父线程中InheritableThreadLocal变量的值。
(3)TransmittableThreadLocal
TransmittableThreadLocal是阿里巴巴开源的一个框架,用于解决在使用线程池等场景下,ThreadLocal变量无法跨线程传递的问题。能够在多线程传递中保持变量的传递性,确保在父线程和子线程之间正确传递ThreadLocal变量。简单的来说就是:ThreadLocal适用于线程内部的数据存储和访问,确保数据在线程间的隔离。InheritableThreadLocal适用于需要在父线程和子线程间传递数据的场景,实现数据的继承。TransmittableThreadLocal则是为了解决在使用线程池等场景下,ThreadLocal变量无法跨线程传递的问题,实现数据的跨线程传递。
参考博客:https://www.cnblogs.com/kukuxjx/p/18188062