【TransmittableThreadLocal】源码分析理解
🏆 前言
上一篇博客【InheritableThreadLocal】搭配线程池使用存在问题,实际开发中遇到,阿里开源类解决这个问题,本文目的是探究一下TTL的原理;
transmittable-thread-local
⭐️ TransmittableThreadLocal
- TransmittableThreadLocal 继承于 InheritableThreadLocal,并拥有了 InheritableThreadLocal 对子线程传递上下文的特性;
- TransmittableThreadLocal中重要属性
holder
;TransmittableThreadLocal
重载了get和set方法,新增了addThisToHolder
和removeThisFromHolder
的逻辑;会把当前这个threadlocal缓存到holder。
initialValue
方法是InheritableThreadLocal
创建时调用,默认创建一个 WeakHashMap。
childValue
方法是创建子线程Thread 调用 init 方法,会调用ThreadLocal.createInheritedMap(parent.inheritableThreadLocals),创建ThreadLocalMap调用childValue
holder保存的是一个WeakHashMap;WeakHashMap的key是在没被强引用的情况下可以被回收的。利用到它的key可以被回收的特性,当作set使用;key存储客户端的多个TransmittableThreadLocal ,值 存 null ;
🎯配合TransmittableThreadLocal
官方提供如下方案:
- 修饰线程池(推荐)
- 修饰Runnable和Callable
- 使用Java Agent来修饰JDK线程池实现类
以修饰线程池为例
修饰线程池调用
- com.alibaba.ttl.threadpool.TtlExecutors#getTtlExecutor
- com.alibaba.ttl.threadpool.ExecutorTtlWrapper#execute 这里对
Runnable
包装了一层TtlRunnable
TtlRunnable
-
TtlRunnable
构造方法,capture
用于捕获父线程的ttl,会将当父线程上下文保存起来;
-
capturedRef.get()
,TtlRunnable 会从 AtomicReference 中获取出调用线程
中所有的上下文(AtomicReference 类似 AtomicInteger ,都是利用CAS实现的原子安全操作)
-
在执行run方法前后,使用TTL做了上下文处理
-
replay
方法
此方法用来给当前子线程将父线程的TTL循环复制进子线程,返回的backup是此子线程原来就有的本地变量值(子线程的TTLMap);
backup用于恢复数据(如果任务执行完毕,意味着该子线程会归还线程池,那么需要将其原生本地变量属性恢复) -
restore
方法用来恢复原有值的
☀️Debug代码理解原理
测试demo
- 🍪 Maven依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.12.0</version>
</dependency>
- 🍍使用新ThreadLocal
private static TransmittableThreadLocal<String> inheritableThreadLocal= new TransmittableThreadLocal();
- 🍋对线程池进行处理
Executor ttlExecutor = TtlExecutors.getTtlExecutor(threadPool);
- 🥝 测试代码
private static InheritableThreadLocal inheritableThreadLocal= new InheritableThreadLocal<>();
private static TransmittableThreadLocal<String> inheritableThreadLocal2= new TransmittableThreadLocal();
@Override
public void TestOne() {
ArrayList<String> supplyDataDtoList = new ArrayList<>();
//模拟写入接口信息
inheritableThreadLocal.set("TestOne");
inheritableThreadLocal2.set("TestOne2");
CompletableFuture.supplyAsync(() -> {
return getPropertyStatisticsSupplyDataList1();
},executor).thenAccept(result -> {
if (result != null && !result.isEmpty()) {
supplyDataDtoList.addAll(result);
}
}).exceptionally(e -> {
logger.error("TestOne" + e.getMessage());
return null;
});
}
@Override
public void TestTwo() {
//模拟写入接口信息
inheritableThreadLocal.set("TestTwo");
ArrayList<String> supplyDataDtoList = new ArrayList<>();
CompletableFuture.supplyAsync(() -> {
return getPropertyStatisticsSupplyDataList();
},executor).thenAccept(result -> {
if (result != null && !result.isEmpty()) {
supplyDataDtoList.addAll(result);
}
}).exceptionally(e -> {
logger.error("TestTwo" + e.getMessage());
return null;
});
}
//异步方法A
private List<String> getPropertyStatisticsSupplyDataList1(){
ArrayList<String> supplyDataDtoList = new ArrayList<>();
//打印inheritableThreadLocal变量
System.out.println("打印异步方法AinheritableThreadLocal变量->"+Thread.currentThread().getName()+inheritableThreadLocal.get());
return supplyDataDtoList;
}
//异步方法B
private List<String> getPropertyStatisticsSupplyDataList(){
ArrayList<String> supplyDataDtoList = new ArrayList<>();
String name=null;
//打印inheritableThreadLocal变量
System.out.println("打印异步方法BinheritableThreadLocal变量->"+Thread.currentThread().getName()+inheritableThreadLocal.get());
System.out.println(name.toString());
return supplyDataDtoList;
}
验证结果
-
先调用
TestOne
接口,设置线程池中的唯一的核心线程中的TTL中的信息; -
调用
TestTwo
接口,在capture
用于捕获父线程的ttl(“TestTwo”),会将当父线程上下文保存起来; -
调用
replay
方法,将子线程中的信息备份(TestOne,TestOne2),将父线程中的TTL(TestTwo)设置到子线程,然后执行run
方法; -
最后执行
restore
方法,将备份的信息(TestOne,TestOne2)设置回子线程