ThreadLoacl,InheritableThreadLocal,原理,以及配合线程池使用的一些坑
之前为了能让InheritableThreadLocal 正确传递,不得不每次
ExecutorService executor = Executors.newFixedThreadPool(>=[任务线程数]);
或者直接new Thread. 这样不仅引起性能损耗,并且如果并发上来了,会造成不必要的上下文切换.还必须用信号量做并发控制.
偶然发现 阿里开源 TransmittableThreadLocal 可以解决此问题.
以下来实验一下
/**
* User: laizhenwei
* Date: 2018-04-12 Time: 10:07
* Description:
*/
public class Ttl { static ExecutorService executorService = Executors.newFixedThreadPool(1); public static void main(String[] args) {
//子线程每次new 所以会复制线程的InheritableThreadLocal,结果正确
// withoutThreadPool(10);
//因线程池复用线程,不会每次new 所以不会更新父线程InheritableThreadLocal 的值,导致结果错误
withThreadPool(10);
} public static void withoutThreadPool(int c){
for(int i=0;i<c;i++){
Integer var1 = (int)(Math.random()*100);
Integer var2 = (int)(Math.random()*100);
MyContextHolder.set(var1);
threadRun(var1,var2);
}
} public static void withThreadPool(int c){
for(int i=0;i<c;i++){
Integer var1 = (int)(Math.random()*100);
Integer var2 = (int)(Math.random()*100);
MyContextHolder.set(var1);
threadPoolExecute(var1,var2);
}
} public static void threadRun(Integer var1,Integer var2){
new Thread(()->assert1(var1,var2)).start();
} public static void threadPoolExecute(Integer var1,Integer var2){
executorService.execute(()->assert1(var1,var2));
} public static void assert1(Integer var1,Integer var2){
System.out.println(MyContextHolder.get()*var2==var1*var2);
} public static class MyContextHolder{ private static ThreadLocal<Integer> stringThreadLocal = new InheritableThreadLocal<>(); public static void set(Integer data) {
stringThreadLocal.set(data);
} public static Integer get() {
return stringThreadLocal.get();
}
} }
withoutThreadPool(10)输出结果
withThreadPool(10); 输出结果
解决方式
pom引入
<!-- https://mvnrepository.com/artifact/com.alibaba/transmittable-thread-local -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.2.0</version>
</dependency>
修改MyContextHolder
public static class MyContextHolder{ private static ThreadLocal<Integer> stringThreadLocal = new TransmittableThreadLocal<>(); // private static ThreadLocal<Integer> stringThreadLocal = new InheritableThreadLocal<>(); public static void set(Integer data) {
stringThreadLocal.set(data);
} public static Integer get() {
return stringThreadLocal.get();
}
}
修改threadPoolExecute
public static void threadPoolExecute(Integer var1,Integer var2){
//使用TransmittableThreadLocal 解决
executorService.execute(TtlRunnable.get(()->assert1(var1,var2)) );
// executorService.execute(()->assert1(var1,var2));
}
运行 withThreadPool(10); 结果