transmittable-thread-local线程透传核心源码解读

transmittale-thread-local简介

transmittable-thread-local是阿里开源的一个ThreadLocal值跨线程透传的组件,主要解决在线程池池化复用线程执行组件的场景下异步执行时上下文传递的问题。支持sdk和agent两种方式接入使用。

不过agent方式接入使用在项目也是需要依赖ttl的TransmittableThreadLocal类,可以将scope设置为provided, 例如:

<dependency>
	<groupId>com.alibaba<groupId>
	<artifactId>transmittable-thread-local</artifactId>
	<version>2.14.5</version>
	<scope>provided</scope>
</dependency>

transmittable-thread-local核心类

其核心逻辑实现都在TransmittableThreadLocal这个类中,除了这个类之外,还有几个包装类需要介绍:

  • ExecutorTtlWrapper:
    java.util.concurrent.Executor的包装类,在执行Executor.execute(Runnable)方法时,会将传入的Runnable对象包装成TtlRunnable。
  • ExecutorServiceTtlWrapper
    java.util.concurrent.ExecutorService的包装类,在执行execute()、subbmit()等相关方法时会将传入参数进行包装,例如Runnable包装成TtlRunnable, Callable包装成TtlCallable。
  • TtlRunnable
    java.lang.Runnable的包装类,主要作用抓取当前线程执行时上下文中注册的ThreadLocal的值(默认为所有的TransmittbleThreadLocal), 在run()方法中将抓取到的ThreadLocal.value设置到当前线程执行时上下文中,具体逻辑下面会说到。
  • TtlCallable
    java.util.concurrent.Callable的包装类,作用与TtlRunnable一样。

核心类

  • TransmittableThreadLocal
    java.lang.InheritableThreadLoal的子类,维护着两个holder静态字段,分别维护着TransmittableThreadLocal和ThreadLocal的注册信息。它自身逻辑没有多少,在get()和set()方法中会向Holder注册自身,remove()方法中则会向Holder取消注册。

  • TransmittableThreadLocal.Transmitter
    负责抓取、传递和恢复在holder中注册过的TransmittableThreadLocal和ThreadLocal的值。同时提供两种ThreadLocal的注册方法。

  • TransmittableThreadLocal.Transmitter.Transmittee
    两种ThreadLocal的抓取、传递和恢复逻辑抽象接口,有两个实现类,分别负责TransmittableThreadLocal和ThreadLocal的抓取、传递和恢复。

transmittalbe-thread-local 透传逻辑解读

ttl的透传实现逻辑其实很简单,将TtlRunnable(TtlCallable也是一样的)的生命周期为分3个阶拆开来看:

  • 新建
    抓取当前线程执行时上下文中的TransmittableThreadLocal和ThreadLocal的值,生成快照。
  • 运行
    运行阶段的最开始,再次抓取当前线程(和新建阶段不是同一个线程了)执行时上下文的快照暂存,然后将新建阶段的快照设置到当前线程执行时上下文中。
  • 结束
    将运行初始阶段暂存的快照恢复到当前线程执行时上下文中。

对应的TlRunnable代码如下:
在这里插入图片描述

当然抓取快照的过程也不是将所有的ThreadLocal的值都抓取,只有指定的ThreadLocal才会被抓取(见TransmittableThreadLocal.registerThreadLocal方法),而TransmittableThreadLocal只要有值默认都会被抓取,因为TransmittableThreadLocal的set()、get()两个方法都会执行一个逻辑:如果TransmittableThreadLocal.get()不为null,就向holder注册。而remove()方法则会向holder取消注册。代码如下:
在这里插入图片描述

那holder又是什么呢?它有什么作用?
其实holder就像是一个注册器,管理当前线程中有被注册过的TransmittableThreadLocal对象,所以它是一个InheritableThreadLocal类型的对象。如下:
在这里插入图片描述
在上面介绍到的抓取快照(capture()方法)、传递(replay()方法)都跟它有关,TransmittableThreadLocal的抓取、传递、恢复都是由threadLocalTransmittee处理的,threadLocalTransmittee是一个匿名内部类,代码如下:

private static final Transmittee<HashMap<ThreadLocal<Object>, Object>, HashMap<ThreadLocal<Object>, Object>> threadLocalTransmittee =
                new Transmittee<HashMap<ThreadLocal<Object>, Object>, HashMap<ThreadLocal<Object>, Object>>() {
                	// 只要是向holder有注册的,就被抓取到快照中
                    @NonNull
                    @Override
                    public HashMap<ThreadLocal<Object>, Object> capture() {
                        final HashMap<ThreadLocal<Object>, Object> threadLocal2Value = newHashMap(threadLocalHolder.size());
                        for (Map.Entry<ThreadLocal<Object>, TtlCopier<Object>> entry : threadLocalHolder.entrySet()) {
                            final ThreadLocal<Object> threadLocal = entry.getKey();
                            final TtlCopier<Object> copier = entry.getValue();

                            threadLocal2Value.put(threadLocal, copier.copy(threadLocal.get()));
                        }
                        return threadLocal2Value;
                    }

					// 快照传递
                    @NonNull
                    @Override
                    public HashMap<ThreadLocal<Object>, Object> replay(@NonNull HashMap<ThreadLocal<Object>, Object> captured) {
                        final HashMap<ThreadLocal<Object>, Object> backup = newHashMap(captured.size());

                        for (Map.Entry<ThreadLocal<Object>, Object> entry : captured.entrySet()) {
                            final ThreadLocal<Object> threadLocal = entry.getKey();
                            backup.put(threadLocal, threadLocal.get());

                            final Object value = entry.getValue();
                            if (value == threadLocalClearMark) threadLocal.remove();
                            else threadLocal.set(value);
                        }

                        return backup;
                    }

					...

					// 恢复快照
                    @Override
                    public void restore(@NonNull HashMap<ThreadLocal<Object>, Object> backup) {
                        for (Map.Entry<ThreadLocal<Object>, Object> entry : backup.entrySet()) {
                            final ThreadLocal<Object> threadLocal = entry.getKey();
                            threadLocal.set(entry.getValue());
                        }
                    }
                };

ThreadLocal的逻辑类似,感兴趣自己看下源码。

其它用法

  1. TransmittableThreadLocal.registerThreadLocalWithShadowCopier(ThreadLocal)
    注册要透传的ThreadLocal对象,ThreadLocal对象中的值会以直接引用的方式传递到当前正在运行的线程执行时上下文中。

  2. TransmittableThreadLocal.registerThreadLocal(ThreadLocal, TtlCopier)
    注册要透传的ThreadLocal对象,同时指定一个TtlCopier对象做为跨线程传递时copier,作用是对ThreadLocal对象中的值进行copy。

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值