阿里TransmittableThreadLocal配合@Async使用过程
作者:蔚蓝色的咸鱼
文章介绍了ThreadLocal、InheritableThreadLocal和TransmittableThreadLocal的区别,并通过实例展示了它们在不同场景下的使用方法
我们都知道ThreadLocal是可以在一个线程中当容器使用的局部变量,是线程隔离、线程安全的。
但是如果子线程要获取父线程的变量,便不太方便
我们分别来看ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal区别
一、ThreadLocal
线程池配置
@Slf4j
@EnableAsync
@Configuration
public class ThreadPoolConfig {
@Bean
public Executor testExecuteAsync(){
ThreadPoolTaskExecutor poolExecutor = new ThreadPoolTaskExecutor();
poolExecutor.setCorePoolSize(5);
poolExecutor.setMaxPoolSize(10);
poolExecutor.setKeepAliveSeconds(10);
poolExecutor.setQueueCapacity(60);
poolExecutor.setThreadNamePrefix("testExecutor-");
poolExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
poolExecutor.initialize();
return poolExecutor;
}
}
ThreadLocal工具类
public class ThreadLocalContext {
private final static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static String get(){
return threadLocal.get();
}
public static void set(String data){
threadLocal.set(data);
}
public static void remove(){
threadLocal.remove();
}
}
测试service
@Service
@Slf4j
public class TestService {
@Async("testExecuteAsync")
public void test() throws InterruptedException {
log.info("父线程内容:{}",ThreadLocalContext.get());
}
}
测试类
@Test
public void test1() throws InterruptedException {
ThreadLocalContext.set("parent:" + Thread.currentThread().getName());
for (int i = 0; i < 5; i++) {
testService.test();
}
Thread.sleep(4000);
}
运行结果

可以看到没有获取到父线程内容
二、InheritableThreadLocal
修改ThreadLocal工具类
public class ThreadLocalContext {
private final static ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
public static String get(){
return threadLocal.get();
}
public static void set(String data){
threadLocal.set(data);
}
public static void remove(){
threadLocal.remove();
}
}
运行程序
@Test
public void test1() throws InterruptedException {
ThreadLocalContext.set("parent:" + Thread.currentThread().getName());
for (int i = 0; i < 5; i++) {
testService.test();
}
Thread.sleep(4000);
}
运行结果

这里是可以获取到父线程内容的
调整TestService代码
@Async("testExecuteAsync")
public void test() throws InterruptedException {
log.info("父线程内容:{}",ThreadLocalContext.get());
//重新设置线程变量中的值
ThreadLocalContext.set("child:" + Thread.currentThread().getName());
}
运行代码
@Test
public void test1() throws InterruptedException {
ThreadLocalContext.set("parent:" + Thread.currentThread().getName());
for (int i = 0; i < 5; i++) {
testService.test();
}
Thread.sleep(4000);
//第二次调用线程获取内容
for (int i = 0; i < 5; i++) {
testService.test();
}
Thread.sleep(4000);
}
运行结果

三、TransmittableThreadLocal
修改线程池配置
注意其中的TtlExecutors.getTtlExecutor( poolExecutor)是使用TTL对线程池进行包裹
@Bean
public Executor testExecuteAsync(){
ThreadPoolTaskExecutor poolExecutor = new ThreadPoolTaskExecutor();
poolExecutor.setCorePoolSize(5);
poolExecutor.setMaxPoolSize(10);
poolExecutor.setKeepAliveSeconds(10);
poolExecutor.setQueueCapacity(60);
poolExecutor.setThreadNamePrefix("testExecutor-");
poolExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
poolExecutor.initialize();
return TtlExecutors.getTtlExecutor(poolExecutor);
}
修改threadLocal工具类
public class ThreadLocalContext {
private final static ThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
public static String get(){
return threadLocal.get();
}
public static void set(String data){
threadLocal.set(data);
}
public static void remove(){
threadLocal.remove();
}
}
运行结果

可以看到线程每次运行都是获取的父线程中的内容
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
