前言
注:在看此篇文章前,你需要了解 ThreadLocal、InheritableThreadLocal
的原理。
这里先总体的介绍TransmittableThreadLocal
(下文以 ttl
作为简称)的原理再去分析一些核心的源码,旨在先有个整体的认识,再去详细了解源码。
整体介绍
复制父线程的 ttl 到 TtlRunnable
由于InheritableThreadLocal
是新建线程时复制父线程的本地变量到子线程,在线程池中由于线程只创建一次,因此无法复制新增的本地变量。
ttl
照着这个思路去找替代方案,在新建线程任务(Runnable)时将这些本地变量放到另一个类的属性字段中保存,这个类就是 TtlRunnable
。
下图的 holder 是一个 InheritableThreadLocal
,存的是当前线程的一个 WeakHashMap
key 为 ttl 对象 value 为 null,在这里可以暂时先忽略,后续再详细介绍。
将 TtlRunnable 保存的 ttl 复制到子线程
在执行 TtlRunnable
的 run()
方法时,再将属性中保存的值通过 TransmittableThreadLocal#set() 方法复制到子线程的 holder 中。需要注意的是 set() 方法并不只复制到 holder,还调用 super.set() 方法,这样相当于在子线程执行一次 ThreadLocal.set(),那么子线程就复制了父线程的本地变量。
子线程如何访问值
通过上一步,已完成从父线程将 ttl
复制到子线程,那就跟 InheritableThreadLocal
类似了,通过 get()
方法就能拿到当前子线程在父类中同一个 ttl
对象对应的值。
小结
父线程通过在新建线程任务时,将父线程所有的 ttl
先保存到 TtlRunnable
对象中,在运行任务时,再将 TtlRunnable
中的值再复制到子线程中,这样子线程就能够访问父线程中同一个 ttl
对象对应的值;这里的话如果不明白没关系,也是先给个整体的框架先了解下,后面再细分析一波就懂了。
需要注意的是,上面的步骤没有提到子线程 holder 的备份与恢复。
详细分析
我们以下面这个例子进行分析,下面代码演示了 TransmittableThreadLocal
的一种基本用法:
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>();
// 父线程设置值
ttl.set("法外狂徒张三");
// 需要使用 TtlRunnable 包装线程任务 Runnable,完成复制
Runnable ttlRunnable = TtlRunnable.get(() -> {
// 获取同一个 ttl 对象在父子线程中的值是否一致
String value = ttl.get();
System.out.println(value);
});
executorService.submit(ttlRunnable);
executorService.shutdown();
}
现在先介绍下刚才说到的 holder:
private static final InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>>
holder =
new InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>>() {
@Override
protected WeakHashMap<TransmittableThreadLocal<Object>, ?> initialValue() {
return new WeakHashMap<TransmittableThreadLocal<Object>, Object>();
}
@Override
protected WeakHashMap<TransmittableThreadLocal<Object>, ?> childValue(WeakHashMap<TransmittableThreadLocal<