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); 結果