前言
在上一篇文章 多线程篇-父子线程的上下文传递
的文末,我们了解到JDK提供的InheritableThreadLocal
在线程池中的使用情况并不是太理想,因为在复用线程的情况下,得到的值很有可能不是我们想要的,接下来我要给大家介绍一款开源组件,阿里开源的,用的感觉还不错。
TransmittableThreadLocal
一般情况下,ThreadLocal都可以满足我们的需求,当我们出现需要 在使用线程池等会池化复用线程的执行组件情况下传递ThreadLocal
,
这个场景就是TransmittableThreadLocal解决的问题。
Github地址:https://github.com/alibaba/transmittable-thread-local
感兴趣的可以去下载的玩一玩,接下来我们来介绍一下这个组件的神奇之处。
首先看个demo, 通过demo,我们先了解了解怎么用
demo
/**
* ttl测试
*
* @author zhangyunhe
* @date 2020-04-23 12:47
*/
public class Test {
// 1. 初始化一个TransmittableThreadLocal,这个是继承了InheritableThreadLocal的
static TransmittableThreadLocal local = new TransmittableThreadLocal<>();
// 初始化一个长度为1的线程池
static ExecutorService poolExecutor = Executors.newFixedThreadPool(1);
public static void main(String[] args) throws ExecutionException, InterruptedException {
Test test = new Test();
test.test();
}
private void test() throws ExecutionException, InterruptedException {
// 设置初始值
local.set("天王老子");
//!!!! 注意:这个地方的Task是使用了TtlRunnable包装的
Future future = poolExecutor.submit(TtlRunnable.get(new Task("任务1")));
future.get();
Future future2 = poolExecutor.submit(TtlRunnable.get(new Task("任务2")));
future2.get();
System.out.println("父线程的值:"+local.get());
poolExecutor.shutdown();
}
class Task implements Runnable{
String str;
Task(String str){
this.str = str;
}
@Override
public void run() {
// 获取值
System.out.println(Thread.currentThread().getName()+":"+local.get());
// 重新设置一波
local.set(str);
}
}
}
输出结果:
pool-1-thread-1:天王老子
pool-1-thread-1:天王老子
父线程的值:天王老子
原理分析
我们首先看一下TransmittableThreadLocal
的源码,
public class TransmittableThreadLocal extends InheritableThreadLocal implements TtlCopier {
// 1. 此处的holder是他的主要设计点,后续在构建TtlRunnable
private static InheritableThreadLocal, ?>> holder =
new InheritableThreadLocal, ?>>() {
@Override
protected WeakHashMap, ?> initialValue() {
return new WeakHashMap, Object>();
}
@Override
protected WeakHashMap, ?> childValue(WeakHashMap, ?> parentValue) {
return new WeakHashMap, Object>(parentValue);
}
};
@SuppressWarnings("unchecked")
private void addThisToHolder() {
if (!holder.get().containsKey(this)) {
holder.get().put((TransmittableThreadLocal