【Java】TransmittableThreadLocal和TtlRunnable源码解析
目录
1 TransmittableThreadLocal和ThreadLocal情况
1.1 ThreadLocal样例
private static Executor EXECUTOR_TMP = Executors.newFixedThreadPool(3);
public static void main(String[] args) {
ThreadLocal<String> value = new ThreadLocal<>();
String tmp1 = "1",tmp2 = "2",tmp3 = "3";
value.set(tmp1);
System.out.println(Thread.currentThread().getName() + " 设置:" + tmp1);
System.out.println(Thread.currentThread().getName() + " 主线程(同步):" + value.get());
// 模拟另一个线程修改上下文内容
EXECUTOR_TMP.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 1子线程(异步):" + value.get());
value.set(tmp2);
System.out.println(Thread.currentThread().getName() + " 设置:" + tmp2);
});
// 保证上面子线程修改成功
sleep(2000);
// 异步获取上下文内容
EXECUTOR_TMP.execute(() -> {
new Thread(() -> System.out.println(Thread.currentThread().getName() + " 2-1子线程(异步):" + value.get())).start();
sleep(2000);
System.out.println(Thread.currentThread().getName() + " 2子线程(异步):" + value.get());
});
// 主线程修改上下文内容
value.set(tmp3);
System.out.println(Thread.currentThread().getName() + " 设置:" + tmp3);
System.out.println(Thread.currentThread().getName() + " 主线程(同步):" + value.get());
EXECUTOR_TMP.execute(() -> {
sleep(3000);
System.out.println(Thread.currentThread().getName() + " 3子线程(异步):" + value.get());
});
EXECUTOR_TMP.execute(() -> {
sleep(300);
System.out.println(Thread.currentThread().getName() + " 4子线程(异步):" + value.get());
});
}
private static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
可以看到,【1子线程(异步)】在没有设值前,拿到的是空值。在设值后才拿到2。【2-1子线程(异步)】、【2子线程(异步)】、【3子线程(异步)】拿到的都是null
1.2 TransmittableThreadLocal样例
private static Executor EXECUTOR_TMP = Executors.newFixedThreadPool(3);
public static void main(String[] args) {
ThreadLocal<String> value = new TransmittableThreadLocal<>();
System.out.println("value类型:" + value.getClass());
String tmp1 = "1",tmp2 = "2",tmp3 = "3";
value.set(tmp1);
System.out.println(Thread.currentThread().getName() + " 设置:" + tmp1);
System.out.println(Thread.currentThread().getName() + " 主线程(同步):" + value.get());
// 模拟另一个线程修改上下文内容
EXECUTOR_TMP.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 1子线程(异步):" + value.get());
value.set(tmp2);
System.out.println(Thread.currentThread().getName() + " 设置:" + tmp2);
});
// 保证上面子线程修改成功
sleep(2000);
// 异步获取上下文内容
EXECUTOR_TMP.execute(() -> {
new Thread(() -> System.out.println(Thread.currentThread().getName() + " 2-1子线程(异步):" + value.get())).start();
sleep(2000);
System.out.println(Thread.currentThread().getName() + " 2子线程(异步):" + value.get());
});
// 主线程修改上下文内容
value.set(tmp3);
System.out.println(Thread.currentThread().getName() + " 设置:" + tmp3);
System.out.println(Thread.currentThread().getName() + " 主线程(同步):" + value.get());
EXECUTOR_TMP.execute(() -> {
new Thread(() -> System.out.println(Thread.currentThread().getName() + " 3-1子线程(异步):" + value.get())).start();
sleep(3000);
System.out.println(Thread.currentThread().getName() + " 3子线程(异步):" + value.get());
});
EXECUTOR_TMP.execute(() -> {
sleep(300);
System.out.println(Thread.currentThread().getName() + " 4子线程(异步):" + value.get());
});
}
private static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
在当前线程中有值,就取值。在当前线程没有值,会去找它的父线程,如果父线程有就取。如果父线程也没有,会再往上找。
2. 在TransmittableThreadLocal下,Runnable和TtlRunnable区别
2.1 代码
private static Executor EXECUTOR_TMP = Executors.newFixedThreadPool(3);
static {
EXECUTOR_TMP = TtlExecutors.getTtlExecutor(EXECUTOR_TMP);
System.out.println("线程池封装ttl");
}
public static void main(String[] args) {
ThreadLocal<String> value = new TransmittableThreadLocal<>();
System.out.println("value类型:" + value.getClass());
String tmp1 = "1",tmp2 = "2",tmp3 = "3";
value.set(tmp1);
System.out.println(Thread.currentThread().getName() + " 设置:" + tmp1);
System.out.println(Thread.currentThread().getName() + " 主线程(同步):" + value.get());
// 模拟另一个线程修改上下文内容
EXECUTOR_TMP.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 1子线程(异步):" + value.get());
value.set(tmp2);
System.out.println(Thread.currentThread().getName() + " 设置:" + tmp2);
});
// 保证上面子线程修改成功
sleep(2000);
// 异步获取上下文内容
EXECUTOR_TMP.execute(() -> {
new Thread(() -> System.out.println(Thread.currentThread().getName() + " 2-1子线程(异步):" + value.get())).start();
sleep(2000);
System.out.println(Thread.currentThread().getName() + " 2子线程(异步):" + value.get());
});
// 主线程修改上下文内容
value.set(tmp3);
System.out.println(Thread.currentThread().getName() + " 设置:" + tmp3);
System.out.println(Thread.currentThread().getName() + " 主线程(同步):" + value.get());
EXECUTOR_TMP.execute(() -> {
new Thread(() -> System.out.println(Thread.currentThread().getName() + " 3-1子线程(异步):" + value.get())).start();
sleep(3000);
System.out.println(Thread.currentThread().getName() + " 3子线程(异步):" + value.get());
});
EXECUTOR_TMP.execute(() -> {
sleep(300);
System.out.println(Thread.currentThread().getName() + " 4子线程(异步):" + value.get());
});
}
private static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
2.2 对比Runnable和TtlRunnable
上面和【1.2的TransmittableThreadLocal样例】
使用Runnable时,同样的线程修改数据后会影响,前面【1子线程(异步)】设置值为2,后面【4子线程(异步)】拿的也是2。看打印的线程名看到是同一个线程。
使用TtlRunnable时,前面【1子线程(异步)】设置值为2,后面【4子线程(异步)】拿的是3,创建TtlRunnable时父线程最后的值。
3. TtlExecutors封装Executor,底层把Runnable封装为TtlRunnable
3.1 封装Executor为ExecutorTtlWrapper
3.2 执行ExecutorTtlWrapper的execute()方法
3.3 Runnable封装为TtlRunnable
4. TtlRunnable初始化
4.1 设值ThreadLocal
4.2 全局保存TransmittableThreadLocal对象
4.3 TtlRunnable初始化
4.4 com.alibaba.ttl.TransmittableThreadLocal.Transmitter#transmitteeSet保存全局TransmittableThreadLocal对象和被保存ThreadLocal对象
说明:【被保存ThreadLocal对象】说的是,ThreadLocal对象用com.alibaba.ttl.TransmittableThreadLocal.Transmitter#registerThreadLocal(java.lang.ThreadLocal
4.5 抓取父线程数据
4.6 运行run()方法时,首先把数据初始化,并且备份
4.7 运行run()方法后,重置数据
- 这是当前线程修改的最新数据
- 这是当前备份的数据
- 重置
参考资料
https://www.cnblogs.com/JohnsonLiu/p/15733680.html
https://blog.csdn.net/whb3299065/article/details/122560431