ThreadLocal在上下文的数据传输上非常的方便和简洁。
工业实践中,比较常用的有三个,ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal,那么他们三个之间有什么区别呢?
ThreadLocal | InheritableThreadLocal | TransmittableThreadLocal | |
来源 | jdk | jdk | 阿里开源 |
单线程数据传输 | 支持 | 支持 | 支持 |
new线程数据传输 | 不支持 | 支持 | 支持 |
线程池数据传输 | 不支持 | 部分支持【简单场景】 | 支持 |
针对线程池的数据传输,InheritableThreadLocal仅仅能在一些简单场景下做到,下面就用一个案例来说明
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.tml.mouseDemo.core.threadLocalDemo;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger;
public class SimpleThreadFactory implements ThreadFactory {
private static AtomicInteger atomicInteger = new AtomicInteger(1); @Override public Thread newThread(Runnable r) {
Thread t = new Thread(r); t.setName("tml-"+atomicInteger.getAndIncrement()); return t; } } |
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
package com.tml.mouseDemo.core.threadLocalDemo;
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
public class ThreadLocalDemo1 {
private static ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
private static ExecutorService service = Executors.newFixedThreadPool(2, new SimpleThreadFactory());
public static void main(String[] args) throws InterruptedException {
threadLocal.set("hello main"); for (int i = 0; i < 2; i++) { service.execute(() -> { String s = threadLocal.get(); ThreadUtils.printLog("get data " + s);
}); }
//修改threadLocal中的值 threadLocal.set("hello world"); Thread.sleep(2000); for (int i = 0; i < 2; i++) {
service.execute(() -> { String s = threadLocal.get(); ThreadUtils.printLog("get data " + s);
}); }
ThreadUtils.printLog("get data " + threadLocal.get());
service.shutdown();
} } |
2025-01-10 19:41:50 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:41:50 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:41:52 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:41:52 | INFO | main | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 19:41:52 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
从运行结果来看,前面的两次循环提交的任务,在子线程中确实是能正常的获取主线程设置的变量,即hello main,但是紧接着,我修改了主线程上绑定的变量为hello world,然后继续循环两次提交两个任务,这个时候子线程中获取的线程变量依然是hello main,这明显是与是期望不一致的。
从这个层面来讲,InheritableThreadLocal确实在线程池的层面支持不够友好,可以说仅支持部分简单场景。
根本原因就死线程池的池化机制,从上面的运行日志上也可以看出,提交了4个任务执行的线程依然是两个。线程池中的线程是复用的,InheritableThreadLocal是在创建子线程的时候,会将主线程上绑定的数据拷贝过来,如果我不创建新的线程,但是主线程上绑定的数据改变了呢?那我依然还是读取到之前拷贝的数据。
这个就是InheritableThreadLocal的短板。针对这个问题,阿里开源的TransmittableThreadLocal就能顺利丝滑的解决这个问题。
?
1 2 3 4 5 |
<dependency> <groupId>com.alibaba</groupId> <artifactId>transmittable-thread-local</artifactId> <version>2.12.4</version> </dependency> |
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
package com.tml.mouseDemo.core.threadLocalDemo;
import com.alibaba.ttl.TransmittableThreadLocal; import com.alibaba.ttl.TtlRunnable;
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
public class ThreadLocalDemo1 {
private static ThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
private static ExecutorService service = Executors.newFixedThreadPool(2, new SimpleThreadFactory());
public static void main(String[] args) throws InterruptedException {
threadLocal.set("hello main"); for (int i = 0; i < 2; i++) { service.execute(TtlRunnable.get(() -> { String s = threadLocal.get(); ThreadUtils.printLog("get data " + s);
})); }
//修改threadLocal中的值 threadLocal.set("hello world"); Thread.sleep(2000); for (int i = 0; i < 2; i++) {
service.execute(TtlRunnable.get(() -> { String s = threadLocal.get(); ThreadUtils.printLog("get data " + s);
})); }
ThreadUtils.printLog("get data " + threadLocal.get());
service.shutdown();
} } |
2025-01-10 19:57:03 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:57:03 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:57:05 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 19:57:05 | INFO | main | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 19:57:05 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
与第一个案例的差异点在于,使用了
?
1 2 3 |
public static TtlRunnable get(@Nullable Runnable runnable) { return get(runnable, false, false); } |
来增强了了Runnable任务,执行的结果也是符合预期。
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
package com.tml.mouseDemo.core.threadLocalDemo;
import com.alibaba.ttl.TransmittableThreadLocal; import com.alibaba.ttl.TtlRunnable; import com.alibaba.ttl.threadpool.TtlExecutors;
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
public class ThreadLocalDemo1 {
private static ThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
private static ExecutorService service = Executors.newFixedThreadPool(2, new SimpleThreadFactory());
private static ExecutorService ttlExecutorService = TtlExecutors.getTtlExecutorService(service);
public static void main(String[] args) throws InterruptedException {
threadLocal.set("hello main"); for (int i = 0; i < 2; i++) { ttlExecutorService.execute(() -> { String s = threadLocal.get(); ThreadUtils.printLog("get data " + s);
}); }
//修改threadLocal中的值 threadLocal.set("hello world"); Thread.sleep(2000); for (int i = 0; i < 2; i++) {
ttlExecutorService.execute(() -> { String s = threadLocal.get(); ThreadUtils.printLog("get data " + s);
}); }
ThreadUtils.printLog("get data " + threadLocal.get());
service.shutdown();
} } |
2025-01-10 20:05:05 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 20:05:05 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 20:05:07 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 20:05:07 | INFO | main | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 20:05:07 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
与上一个案例的差异点在于,这里没有包装Runnable任务,而是包装了线程池,使用了
?
1 2 3 4 5 6 |
public static ExecutorService getTtlExecutorService(@Nullable ExecutorService executorService) { if (TtlAgent.isTtlAgentLoaded() || executorService == null || executorService instanceof TtlEnhanced) { return executorService; } return new ExecutorServiceTtlWrapper(executorService, true); } |
包装了ExecutorService,执行结果也是符合预期
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package com.tml.mouseDemo.core.threadLocalDemo;
import com.alibaba.ttl.TransmittableThreadLocal;
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
public class ThreadLocalDemo1 {
private static ThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
private static ExecutorService service = Executors.newFixedThreadPool(2, new SimpleThreadFactory());
public static void main(String[] args) throws InterruptedException {
threadLocal.set("hello main"); for (int i = 0; i < 2; i++) { service.execute(() -> { String s = threadLocal.get(); ThreadUtils.printLog("get data " + s);
}); }
//修改threadLocal中的值 threadLocal.set("hello world"); Thread.sleep(2000); for (int i = 0; i < 2; i++) {
service.execute(() -> { String s = threadLocal.get(); ThreadUtils.printLog("get data " + s);
}); }
ThreadUtils.printLog("get data " + threadLocal.get());
service.shutdown();
} } |
项目运行的时候,需要添加额外的jvm启动参数,如下
2025-01-10 20:11:59 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 20:11:59 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 20:12:01 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 20:12:01 | INFO | main | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 20:12:01 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
阿里巴巴的TransmittableThreadLocal是继承自InheritableThreadLocal,对他的功能进行了增强,增强的点也主要是在线程池的支持上。
通过上面的三个案例,可以看到TransmittableThreadLocal是非常灵活的,大家可以根据自己的需要,选择对应的方式来实现。