大家好,我叫大鸡腿,大家可以关注下我,会持续更新技术文章还有人生感悟,感谢~
文章目录
前言
TransmittableThreadLocal是阿里开源的一个类,主要目的是处理父子线程变量不能共用的情况。ThreadLocal是跟当前线程挂钩的,所以脱离当前线程它就起不了作用。
ThreadLocal
场景:它的应用就是比如当前用户特有的一些属性,不能跟其他线程,用户共用。
TransmittableThreadLocal
场景:就是父子线程或者不同线程需要共用一些变量。举个例子:之前在肥朝哥群里,肥大一直叼胃口,因为全链路日志是自研的嘛,没有sleuth等等框架的支持,所以像MDC这些全局变量,在多线程里头就失效了。
有人觉得打印log不就完事了吗?
链路日志就是为了把请求参数,整个链路打印出来,看出性能问题,以及报错的前因后果。
有了TransmittableThreadLocal就不破自解了。
TransmittableThreadLocal
源码走起
比较关键就是在get,set方法里头。我们看下addValue方法是哪路神仙。
如果当前holder没有储存当前线程,那么就把当前线程set到这个holder里头,我们再看下holder是何方神圣。
其实是InheritableThreadLocal,里头包裹一个Map,类型是TransmittableThreadLocal。
这个有什么用呢?
TransmittableThreadLocal跟InheritableThreadLocal的区别?
InheritableThreadLocal解决父子线程的问题,它是在线程创建的时候进行复制上下文的。那么对于线程池的已经创建完了就无从下手了,所以在线程提交的时候要进行上下文的复制。这就是TransmittableThreadLocal想要解决的问题。
任务提交给线程池时的ThreadLocal值传递过程
1.TtlRunnable
在创建的时候,copiedRef会将上下文保存起来。
private final AtomicReference<Map<TransmittableThreadLocal<?>, Object>> copiedRef;
2.copy
这一块就是解释上面holder的作用,将值取出来赋值给copiedRef
3.run
在提交的时候进行复制上下文。
4.backupAndSetToCopied
到这里就完成上下文ThreadLocal的值进行复制了。
maven
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.2.0</version>
</dependency>
demo
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.TtlRunnable;
import java.util.concurrent.TimeUnit;
public class TtlSample1 {
static ThreadLocal<String> TTL = new TransmittableThreadLocal<>();
public static void main(String[] args) throws Exception {
new Thread(() -> {
// 在父线程中设置变量
TTL.set("throwable");
new Thread(() -> {
methodFrame1();
}).start();
try {
TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
private static void methodFrame1() {
methodFrame2();
}
private static void methodFrame2() {
System.out.println(TTL.get());
}
}
他会打印throwable,如果是ThreadLocal TTL = new ThreadLocal<>()的话,返回的是null。
解读
其实上面是InheritableThreadLocal父子线程起作用
ExecutorService executorService = Executors.newCachedThreadPool();
// 额外的处理,生成修饰了的对象executorService
executorService = TtlExecutors.getTtlExecutorService(executorService);
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<String>();
context.set("value-set-in-parent");
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println(context.get());
}
};
executorService.submit(task);
针对线程池,需要使用TtlExecutors进行改造。