InheritableThreadLocal虽然能解决父子线程中的对象拷贝问题,并且自己再重新childValue方法还可以实现深拷贝。但是大多数情况下我们是使用的自定义线程池,而不是方法执行过程中再去创建线程。还好阿里提供了TransmittableThreadLocal解决该问题,需要引入依赖包
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.11.5</version>
</dependency>
先来一个demo,还是使用上一篇博客中InheritableThreadLocal不能解决的问题,如下:
static ThreadPoolExecutor executorService = (ThreadPoolExecutor)Executors.newFixedThreadPool(5);
static {
executorService.prestartAllCoreThreads();
}
public static void main(String[] args) throws InterruptedException {
TransmittableThreadLocal<String> itl = new TransmittableThreadLocal<>();
itl.set("kevin-2");
// 使用TtlRunnable或TtlCallable包装原生回调方法
executorService.execute(Objects.requireNonNull(TtlRunnable.get(() -> {
System.out.println("InheritableThreadLocal ThreadPoolExecutor by TtlRunnable:" + itl.get());
})));
// 使用包装TtlExecutors包装原生的ThreadPoolExecutor
// 1、getTtlExecutor:修饰接口Executor
// 2、getTtlExecutorService:修饰接口ExecutorService
// 3、getTtlScheduledExecutorService:修饰接口ScheduledExecutorService
Executor ttlExecutor = TtlExecutors.getTtlExecutor(executorService);
ttlExecutor.execute(() -> {
System.out.println("InheritableThreadLocal ThreadPoolExecutor by TtlExecutors:" + itl.get());
});
Thread.sleep(10000);
}
InheritableThreadLocal ThreadPoolExecutor by TtlRunnable:kevin-2
InheritableThreadLocal ThreadPoolExecutor by TtlExecutors:kevin-2
可见拿到了值,并且提供了对原生 ThreadPoolExecutor、Runnable、Callable的支持。只是将原对象进行包装即可,非常的方便。
使用场景:
1、分布式跟踪系统
2、应用容器或上层框架跨应用代码给下层SDK传递信息
3、日志收集记录系统上下文
4、
之前自己在项目上,在Tomcat线程池的线程中,使用自定义的线程池处理并行任务时,想在主线程中将入参(中途不会进行修改入参)放入ThreadLocal或其子类中,在并行任务的线程池中获取参数,但是发送拿不到。
5、
也是相同的场景,只是需要在子方法中获取父线程是否已经开始事务,以处理子线程中的事务问题,同样也是拿不到
项目实例:
当我在项目中使用 Java8的工具处理线程任务时,由于是自行封装的回调函数,还以为在线程池任务中也是不能获取到任务。结果是因为自己在初始化TransmittableThreadLocal时使用了设置初始化值,如下:
// 这样线程池中拿到的也是空数据
private static final TransmittableThreadLocal THREAD_LOCAL = TransmittableThreadLocal.withInitial(() -> null)
项目实例如下:
CompletableFuture.supplyAsync(回调函数, 线程池对象ThreadPoolExecutor);