- 前言
- ⭐️ TransmittableThreadLocal
- 配合TransmittableThreadLocal
- 以修饰线程池为例
- 修饰线程池调用
- TtlRunnable
- ☀️Debug代码理解原理
- 测试demo
- 验证结果
前言
上一篇博客【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
配合TransmittableThreadLocalholder保存的是一个WeakHashMap;WeakHashMap的key是在没被强引用的情况下可以被回收的。利用到它的key可以被回收的特性,当作set使用;key存储客户端的多个TransmittableThreadLocal ,值 存 null ;
官方提供如下方案:
- 修饰线程池(推荐)
- 修饰Runnable和Callable
- 使用Java Agent来修饰JDK线程池实现类
- com.alibaba.ttl.threadpool.TtlExecutors#getTtlExecutor
- com.alibaba.ttl.threadpool.ExecutorTtlWrapper#execute 这里对Runnable包装了一层TtlRunnable
-
TtlRunnable构造方法,capture用于捕获父线程的ttl,会将当父线程上下文保存起来;
-
capturedRef.get(),TtlRunnable 会从 AtomicReference 中获取出调用线程中所有的上下文(AtomicReference 类似 AtomicInteger ,都是利用CAS实现的原子安全操作)
-
在执行run方法前后,使用TTL做了上下文处理
-
replay方法
此方法用来给当前子线程将父线程的TTL循环复制进子线程,返回的backup是此子线程原来就有的本地变量值(子线程的TTLMap);
backup用于恢复数据(如果任务执行完毕,意味着该子线程会归还线程池,那么需要将其原生本地变量属性恢复) -
restore方法用来恢复原有值的
- Maven依赖
com.alibaba transmittable-thread-local 2.12.0
- 使用新ThreadLocal
private static TransmittableThreadLocalinheritableThreadLocal= new TransmittableThreadLocal();
- 对线程池进行处理
Executor ttlExecutor = TtlExecutors.getTtlExecutor(threadPool);
- 諾 测试代码
private static InheritableThreadLocal inheritableThreadLocal= new InheritableThreadLocal<>(); private static TransmittableThreadLocal验证结果inheritableThreadLocal2= new TransmittableThreadLocal(); @Override public void TestOne() { ArrayList 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 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 getPropertyStatisticsSupplyDataList1(){ ArrayList supplyDataDtoList = new ArrayList<>(); //打印inheritableThreadLocal变量 System.out.println("打印异步方法AinheritableThreadLocal变量->"+Thread.currentThread().getName()+inheritableThreadLocal.get()); return supplyDataDtoList; } //异步方法B private List getPropertyStatisticsSupplyDataList(){ ArrayList 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)设置回子线程



