多线程-父子线程传递参数
场景
父子线程需要传递参数的场景有很多:
- 分布式系统中链路追踪,获取调用链路上的 traceId
- 日志系统中每个方法调用时上下文追踪,如 MDC
ThreadLocal
我们知道 ThreadLocal 可以解决在同一个线程的上下文传递参数,但是在多线程环境下,这显然就不行了
public class ThreadInherit {
public static ThreadLocal<String> local = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
System.out.println("current Thread :" + Thread.currentThread().getName());
local.set("hello");
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("task1.........");
System.out.println("sub Thread :" + Thread.currentThread().getName() + " context: " + local.get());
}
}).start();
TimeUnit.MICROSECONDS.sleep(300);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("task2.........");
System.out.println("sub Thread :" + Thread.currentThread().getName() + " context: " + local.get());
}
}).start();
}
}
运行结果:
current Thread :main
task1.........
sub Thread :Thread-0 context: null
sub Thread :Thread-0 inherit context: world
task2.........
sub Thread :Thread-1 context: null
sub Thread :Thread-1 inherit context: world
phase1 current Thread :main inherit context: world
可以发现, ThreadLocal 并不能解决线程之间的上下文传递
InheritableThreadLocal
这个是 ThreadLocal 的子类, 可以在父子线程之间参数传递
public class ThreadInherit {
public static ThreadLocal<String> local = new ThreadLocal<>();
public static ThreadLocal<String> inheritLocal = new InheritableThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
System.out.println("current Thread :" + Thread.currentThread().getName());
local.set("hello");
inheritLocal.set("world");
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("task1.........");
System.out.println("sub Thread :" + Thread.currentThread().getName() + " context: " + local.get());
System.out.println("sub Thread :" + Thread.currentThread().getName() + " inherit context: " + inheritLocal.get());
inheritLocal.set("task1");
}
}).start();
TimeUnit.MICROSECONDS.sleep(300);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("task2.........");
System.out.println("sub Thread :" + Thread.currentThread().getName() + " context: " + local.get());
System.out.println("sub Thread :" + Thread.currentThread().getName() + " inherit context: " + inheritLocal.get());
}
}).start();
TimeUnit.SECONDS.sleep(2);
System.out.println("phase1 current Thread :" + Thread.currentThread().getName() + " inherit context: " + inheritLocal.get());
}
}
结果:
current Thread :main
task1.........
sub Thread :Thread-0 context: null
sub Thread :Thread-0 inherit context: world
task2.........
sub Thread :Thread-1 context: null
sub Thread :Thread-1 inherit context: world
phase1 current Thread :main inherit context: world
可以发现, InheritableThreadLocal 可以解决线程之间上下文传递
但是还有一种情况:
public class ThreadInherit {
public static ThreadLocal<String> local = new ThreadLocal<>();
public static ThreadLocal<String> inheritLocal = new InheritableThreadLocal<>();
public static ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(1);
public static void main(String[] args) throws InterruptedException {
System.out.println("current Thread :" + Thread.currentThread().getName());
local.set("hello");
inheritLocal.set("world");
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("task1.........");
System.out.println("sub Thread :" + Thread.currentThread().getName() + " context: " + local.get());
System.out.println("sub Thread :" + Thread.currentThread().getName() + " inherit context: " + inheritLocal.get());
inheritLocal.set("task1");
}
}).start();
TimeUnit.MICROSECONDS.sleep(300);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("task2.........");
System.out.println("sub Thread :" + Thread.currentThread().getName() + " context: " + local.get());
System.out.println("sub Thread :" + Thread.currentThread().getName() + " inherit context: " + inheritLocal.get());
}
}).start();
TimeUnit.SECONDS.sleep(2);
System.out.println("phase1 current Thread :" + Thread.currentThread().getName() + " inherit context: " + inheritLocal.get());
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println("task3......");
System.out.println("sub Thread :" + Thread.currentThread().getName() + " context: " + local.get());
System.out.println("sub Thread :" + Thread.currentThread().getName() + " inherit context: " + inheritLocal.get());
inheritLocal.set("task3");
}
});
TimeUnit.MICROSECONDS.sleep(300);
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println("task4......");
System.out.println("sub Thread :" + Thread.currentThread().getName() + " context: " + local.get());
System.out.println("sub Thread :" + Thread.currentThread().getName() + " inherit context: " + inheritLocal.get());
}
});
TimeUnit.SECONDS.sleep(2);
System.out.println("phase2 current Thread :" + Thread.currentThread().getName() + " inherit context: " + inheritLocal.get());
TimeUnit.SECONDS.sleep(2);
}
}
结果:
current Thread :main
task1.........
sub Thread :Thread-0 context: null
sub Thread :Thread-0 inherit context: world
task2.........
sub Thread :Thread-1 context: null
sub Thread :Thread-1 inherit context: world
phase1 current Thread :main inherit context: world
task3......
sub Thread :pool-1-thread-1 context: null
sub Thread :pool-1-thread-1 inherit context: world
task4......
sub Thread :pool-1-thread-1 context: null
sub Thread :pool-1-thread-1 inherit context: task3
phase2 current Thread :main inherit context: world
在线程池环境下,main thread 居然没有变成 task3, 这显然是不对的, 那如何解决呢?
TransmittableThreadLocal
TransmittableThreadLocal是阿里开源的一个组件,TransmittableThreadLocal 继承 InheritableThreadLocal,使用方式也类似,解决了在使用线程池等会池化复用线程的执行组件情况下传递ThreadLocal值的问题