【TransmittableThreadLocal】源码分析理解


🏆 前言

上一篇博客【InheritableThreadLocal】搭配线程池使用存在问题,实际开发中遇到,阿里开源类解决这个问题,本文目的是探究一下TTL的原理;
transmittable-thread-local
在这里插入图片描述


⭐️ TransmittableThreadLocal

  • TransmittableThreadLocal 继承于 InheritableThreadLocal,并拥有了 InheritableThreadLocal 对子线程传递上下文的特性;
    在这里插入图片描述
  • TransmittableThreadLocal中重要属性holderTransmittableThreadLocal重载了get和set方法,新增了addThisToHolderremoveThisFromHolder的逻辑;会把当前这个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线程池实现类

以修饰线程池为例

在这里插入图片描述

修饰线程池调用

  1. com.alibaba.ttl.threadpool.TtlExecutors#getTtlExecutor在这里插入图片描述
  2. com.alibaba.ttl.threadpool.ExecutorTtlWrapper#execute 这里对Runnable包装了一层TtlRunnable在这里插入图片描述

TtlRunnable

  1. TtlRunnable构造方法,capture用于捕获父线程的ttl,会将当父线程上下文保存起来;
    在这里插入图片描述

  2. capturedRef.get(),TtlRunnable 会从 AtomicReference 中获取出调用线程中所有的上下文(AtomicReference 类似 AtomicInteger ,都是利用CAS实现的原子安全操作)
    在这里插入图片描述

  3. 在执行run方法前后,使用TTL做了上下文处理在这里插入图片描述

  4. replay方法
    此方法用来给当前子线程将父线程的TTL循环复制进子线程,返回的backup是此子线程原来就有的本地变量值(子线程的TTLMap);
    backup用于恢复数据(如果任务执行完毕,意味着该子线程会归还线程池,那么需要将其原生本地变量属性恢复)在这里插入图片描述

  5. 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;
    }

验证结果

  1. 先调用TestOne接口,设置线程池中的唯一的核心线程中的TTL中的信息;在这里插入图片描述

  2. 调用TestTwo接口,在capture用于捕获父线程的ttl(“TestTwo”),会将当父线程上下文保存起来;在这里插入图片描述

  3. 调用 replay方法,将子线程中的信息备份(TestOne,TestOne2),将父线程中的TTL(TestTwo)设置到子线程,然后执行run方法;在这里插入图片描述

  4. 最后执行restore方法,将备份的信息(TestOne,TestOne2)设置回子线程在这里插入图片描述

### TransmittableThreadLocal源码分析 #### 背景介绍 `TransmittableThreadLocal` 是阿里巴巴开源的一个工具类库 `ttl` 中的核心组件,用于解决 Java 线程池场景下的线程本地变量传递问题。传统的 `ThreadLocal` 和 `InheritableThreadLocal` 存在线程池复用时无法正确传递上下文变量的问题[^1]。 为了克服这一缺陷,`TransmittableThreadLocal` 将线程局部变量的传递时机从线程初始化阶段推迟到任务实际执行阶段,从而确保即使在使用线程池的情况下也能正常传递上下文变量[^3]。 --- #### 核心设计思路 `TransmittableThreadLocal` 的核心设计理念在于动态绑定和解绑线程局部变量。它通过拦截器机制,在任务提交给线程池之前捕获当前线程的上下文变量,并将其注入目标线程;当任务完成后自动清理这些变量以防止内存泄漏[^4]。 以下是其主要功能模块: 1. **上下文捕获与恢复** - 当主线程向线程池提交任务时,会先捕获当前线程的所有 `TransmittableThreadLocal` 变量。 - 这些变量会被封装成一个副本并附加到任务上。 - 在子线程执行任务前,该副本被重新应用到子线程中。 2. **自动清理机制** - 为了避免因线程池复用而导致的内存泄露,`TransmittableThreadLocal` 提供了显式的清除逻辑。 - 使用完毕后,可以通过调用 `remove()` 方法释放资源[^2]。 3. **兼容传统 API** - 它完全兼容标准的 `ThreadLocal` 接口,开发者无需修改现有代码即可无缝切换至支持跨线程传播的功能。 --- #### 源码解析 ##### 类结构概览 ```java public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> { private static final CopyOnInheritContext COPY_ON_INHERIT_CONTEXT = new CopyOnInheritContext(); } ``` - 继承自 `InheritableThreadLocal`,保留了父类的行为特性。 - 静态成员变量 `COPY_ON_INHERIT_CONTEXT` 实现了上下文复制逻辑。 --- ##### 关键方法详解 ###### 1. 设置值 (`set`) ```java @Override protected T childValue(T parentValue) { return this.copyValue(parentValue); } private T copyValue(T value) { if (value instanceof Cloneable) { try { Method cloneMethod = value.getClass().getMethod("clone"); return (T) cloneMethod.invoke(value); } catch (Exception e) { throw new RuntimeException(e); } } return value; } ``` - 重写了 `childValue` 方法,定义了如何将父线程的值传递给子线程。 - 如果值实现了 `Cloneable` 接口,则尝试克隆一份新实例返回,否则直接返回值。 --- ###### 2. 获取值 (`get`) ```java @Override public T get() { T value = super.get(); if (value == null && !isInitialValueInvoked()) { value = initialValue(); set(value); } return value; } ``` - 调用了父类的 `get` 方法获取当前线程上的值。 - 若未设置过初始值,则调用 `initialValue()` 自动初始化。 --- ###### 3. 清除值 (`remove`) ```java @Override public void remove() { super.remove(); clearAllCopiedValues(); } private void clearAllCopiedValues() { for (CopyOnInheritContext context : contextsToClear) { context.clear(); } contextsToClear.clear(); } ``` - 不仅清除了本线程上的值,还同步清理了所有已复制的上下文数据。 - 此操作对于预防潜在的内存溢出至关重要。 --- #### 工作流程图示 以下是 `TransmittableThreadLocal` 的典型工作流: 1. 主线程 A 创建了一个 `TransmittableThreadLocal` 并设置了某些值 X。 2. 主线程 A 向线程池提交任务 Task。 3. 在任务启动前,框架截取主线程 A 上的所有 TTL 值形成快照 S。 4. 子线程 B 开始运行 Task 时加载快照 S 到自己的作用域内。 5. 执行结束后销毁临时保存的数据以防污染后续任务。 --- ### 总结 通过对 `TransmittableThreadLocal` 的深入剖析可以发现,它的创新之处在于引入了一种延迟绑定策略,解决了多线程环境下共享状态难以管理的技术难题。同时提供了完善的生命周期控制手段,极大地方便了分布式系统的开发人员处理复杂的业务场景。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Abner G

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值