我们都知道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();
}
}
运行结果
可以看到线程每次运行都是获取的父线程中的内容