思维导图
系列总目录
背景
在使用线程池等会池化复用线程的执行组件情况下,transmittable-thread-local(简称TTL)提供ThreadLocal值的传递功能,解决异步执行时上下文传递的问题。TTL主要解决了:
- 透传上下文信息,run方法调用结束时自动restore上下文信息
- 线程池使用CallerRunsPolicy时,避免了手动回收上下文信息会将主线程的上下文信息清空
使用
// 使用TransmittableThreadLocal
static TransmittableThreadLocal<String> transmittableThreadLocal = new TransmittableThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
// 用TtlExecutors装饰线程池
executorService = TtlExecutors.getTtlExecutorService(executorService);
transmittableThreadLocal.set("i am a transmittable parent");
executorService.execute((Runnable) () -> {
System.out.println(transmittableThreadLocal.get());
// 子线程设置新的值
transmittableThreadLocal.set("i am a old transmittable parent");
});
System.out.println(transmittableThreadLocal.get());
TimeUnit.SECONDS.sleep(1);
// 主线程设置新的值
transmittableThreadLocal.set("i am a new transmittable parent");
executorService.execute((Runnable) () -> System.out.println(transmittableThreadLocal.get()));
}
i am a transmittable parent
i am a transmittable parent
i am a new transmittable parent
- 执行代码后发现,使用TTL和TtlExecutors.getTtlExecutorService(executorService)装饰线程池之后,在每次调用任务的时,都会将当前的主线程的TTL数据copy到子线程里面,执行完成后,再清除掉。同时子线程里面的修改回到主线程时其实并没有生效。这样可以保证每次任务执行的时候都是互不干涉的
源码
-
完整时序图
- 线程池执行时,执行了 ExecutorTtlWrapper execute 方法,execute 方法中调用了 4.1.1,4.1.1 方法中创建了一个 TtlRunnable 对象返回了。
TtlRunnable 构造方法中,调用了 TransmittableThreadLocal.Transmitter.capture() 获取当前线程中所有的上下文,并储存在 AtomicReference 中。 - 当线程执行时,调用 TtlRunnable run 方法,TtlRunnable 会从 AtomicReference 中获取出调用线程中所有的上下文,并把上下文给 TransmittableThreadLocal.Transmitter.replay 方法把上下文复制到当前线程。并把上下文备份。
- 当线程执行完,调用 TransmittableThreadLocal.Transmitter.restore 并把备份的上下文传入,恢复备份的上下文,把后面新增的上下文删除,并重新把上下文复制到当前线程。