【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)设置回子线程在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Abner G

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

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

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

打赏作者

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

抵扣说明:

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

余额充值