栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【TransmittableThreadLocal】源码分析理解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【TransmittableThreadLocal】源码分析理解

【TransmittableThreadLocal】源码分析理解
  •  前言
  • ⭐️ 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

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依赖
		
			com.alibaba
			transmittable-thread-local
			2.12.0
		
  • 使用新ThreadLocal
private static TransmittableThreadLocal inheritableThreadLocal=    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;
    }
验证结果
  1. 先调用TestOne接口,设置线程池中的唯一的核心线程中的TTL中的信息;

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

  3. 调用 replay方法,将子线程中的信息备份(TestOne,TestOne2),将父线程中的TTL(TestTwo)设置到子线程,然后执行run方法;

  4. 最后执行restore方法,将备份的信息(TestOne,TestOne2)设置回子线程

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1007903.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号