1. 程式人生 > 實用技巧 >Java 學習筆記(三)之 ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal

Java 學習筆記(三)之 ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal

一、初識 服務程式是由程序構成,程序是由無數個執行緒構成,執行緒是一組程式碼片段組成。在Java的多執行緒程式設計中,為保證多個執行緒對共享變數的安全訪問,通常會使用synchronized來保證同一時刻只有一個執行緒對共享變數進行操作。這種情況下可以將類變數放到ThreadLocal型別的物件中,使變數在每個執行緒中都有獨立拷貝,不會出現一個執行緒讀取變數時而被另一個執行緒修改的現象。 需要注意的是: 若 ThreadLocal 儲存的變數是引用變數,可以對所有執行緒中的threadLocal 通過 set() 方法 設定同一個引用變數,這樣所有執行緒共享一個變數。但是要求該變數必須是執行緒安全的,否則還是會出現資料執行緒安全問題。
進一步思考的問題: 多個執行緒怎麼共享使用同一資料(存放在某個引用變數中),並可以執行緒安全的操作該引用變數?(通過 ThreadLocal 就可以達到我們的目的嗎?) 後續統一稱 執行緒定義的ThreadLocal 變數為 執行緒本地變數,而ThreadLocal 通過 set方法設定的變數稱為 value變數。

二、深入解析原理

背景:父執行緒定義了執行緒本地變數(ThreadLocal)用於儲存各個子執行緒的執行結果,子執行緒使用了執行緒池,父執行緒是web 容器執行緒池中的執行緒。 (1) 目前使用 ThreadLocal 作為執行緒本地變數,發現子執行緒無法訪問父執行緒 ThreadLocal 中定義的 value 變數; (2) 將父執行緒中的 ThreadLocal ---> InheritableThreadLocal ,此時子執行緒可以訪問父執行緒本地變數中的value變數,但由於子執行緒來自執行緒池,子執行緒是複用的,導致執行完所有子執行緒,在父執行緒訪問區域性變數值,發現值不是子執行緒中設定的結果值,而是之前的; 目標
:每次執行父執行緒程式碼中的邏輯,都會把父執行緒區域性變數值重新清空,用於承載其開啟的多個子執行緒的結果值,要求所有子執行緒執行完畢之後,父執行緒區域性變數能夠正確看到所有子執行緒執行的結果。 示例程式碼片段:
 1 @RestController
 2 @RequestMapping("/threadLocal")
 3 public class ThreadLocalExample {
 4     private ThreadLocal<Map<String,Student>> parentThreadLocal;
 5     private ExecutorService executor;
6 private Gson gson = new Gson(); 7 private static final Logger logger = LoggerFactory.getLogger(ThreadLocalExample.class); 8 9 public ThreadLocalExample(){ 10 parentThreadLocal = new ThreadLocal<>(); 11 executor = Executors.newFixedThreadPool(2); 12 } 13 14 @GetMapping("") 15 public Map<String,Student> multi(){ 16 parentThreadLocal.set(Maps.newConcurrentMap()); 17 logger.info("value of parentThreadLocal in parentThread is [{}]",gson.toJson(parentThreadLocal.get())); 18 List<Future<Void>> threadResults = Lists.newArrayList(); 19 for(int i =0 ;i<=2;i++){ 20 threadResults.add(executor.submit(new SubThread("Thread"+i))); 21 } 22 23 for(Future<Void> future:threadResults){ 24 try { 25 future.get(); 26 } catch (InterruptedException | ExecutionException e) { 27 logger.error(e.getMessage(),e); 28 } 29 } 30 logger.info("value of parentThreadLocal in parentThread is [{}]",gson.toJson(parentThreadLocal.get())); 31 return parentThreadLocal.get(); 32 } 33 34 private final class SubThread implements Callable<Void>{ 35 private String identification; 36 37 public SubThread(String identification){ 38 this.identification = identification; 39 } 40 41 @Override 42 public Void call() throws Exception { 43 logger.info("start to execute thread[{}]",identification); 44 logger.info("value of parentThreadLocal in subThread is [{}]",gson.toJson(parentThreadLocal.get())); 45 Student subStudent = new Student(); 46 subStudent.setName(Thread.currentThread().getName()); 47 if(parentThreadLocal.get() != null){ 48 parentThreadLocal.get().put(identification, subStudent); 49 } 50 logger.info("end:value of parentThreadLocal in subThread is [{}]",gson.toJson(parentThreadLocal.get())); 51 return null; 52 } 53 } 54 }
執行結果:
 1 2020-05-24T09:54:54.931+08:00 INFO [http-nio-8080-exec-1] [com.example.demo.controller.ThreadLocalExample.multi:38] - value of parentThreadLocal in parentThread is [{}]
 2 2020-05-24T09:54:54.938+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.ThreadLocalExample.call:64] - start to execute thread[Thread1]
 3 2020-05-24T09:54:54.938+08:00 INFO [pool-1-thread-1] [com.example.demo.controller.ThreadLocalExample.call:64] - start to execute thread[Thread0]
 4 2020-05-24T09:54:54.938+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.ThreadLocalExample.call:65] - value of parentThreadLocal in subThread is [null]
 5 2020-05-24T09:54:54.938+08:00 INFO [pool-1-thread-1] [com.example.demo.controller.ThreadLocalExample.call:65] - value of parentThreadLocal in subThread is [null]
 6 2020-05-24T09:54:54.938+08:00 INFO [pool-1-thread-1] [com.example.demo.controller.ThreadLocalExample.call:71] - end:value of parentThreadLocal in subThread is [null]
 7 2020-05-24T09:54:54.938+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.ThreadLocalExample.call:71] - end:value of parentThreadLocal in subThread is [null]
 8 2020-05-24T09:54:54.938+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.ThreadLocalExample.call:64] - start to execute thread[Thread2]
 9 2020-05-24T09:54:54.939+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.ThreadLocalExample.call:65] - value of parentThreadLocal in subThread is [null]
10 2020-05-24T09:54:54.939+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.ThreadLocalExample.call:71] - end:value of parentThreadLocal in subThread is [null]
11 2020-05-24T09:54:54.939+08:00 INFO [http-nio-8080-exec-1] [com.example.demo.controller.ThreadLocalExample.multi:51] - value of parentThreadLocal in parentThread is [{}]
1、ThreadLocal (1) 程式碼示例
 1 package com.example.demo.controller.threadlocalexample;
 2 
 3 import java.util.Map;
 4 import java.util.concurrent.CountDownLatch;
 5 
 6 import org.springframework.web.bind.annotation.GetMapping;
 7 import org.springframework.web.bind.annotation.RequestMapping;
 8 import org.springframework.web.bind.annotation.RestController;
 9 
10 import com.example.demo.dao.entity.Student;
11 import com.google.common.collect.Maps;
12 import com.google.gson.Gson;
13 
14 import lombok.extern.slf4j.Slf4j;
15 
16 /**
17  * <b>ThreadLocal 用法例子</b>
18  * <pre>
19  *         父執行緒定義的ThreadLocal變數裡的value值不會被子執行緒使用,在示例中子執行緒給父執行緒 ThreadLocal變數設定值設定不了
20  * </pre>
21  *
22  */
23 @RestController
24 @RequestMapping("/threadLocal")
25 @Slf4j
26 public class ThreadLocalExample {
27     /**ThreadLocal 變數***/
28     private ThreadLocal<Map<String,Student>> parentThreadLocal;
29     private Gson gson = new Gson();
30     
31     public ThreadLocalExample(){
32         parentThreadLocal = new ThreadLocal<>();
33     }
34     
35     @GetMapping
36     public Map<String,Map<String,Student>> multi(){
37         parentThreadLocal.set(Maps.newConcurrentMap());
38         log.info("value of parentThreadLocal in parentThread is [{}]",gson.toJson(parentThreadLocal.get()));
39         CountDownLatch latch = new CountDownLatch(3);
40         for(int i =0 ;i<=2;i++){
41             new Thread(new SubThread("Thread"+i,latch)).start();;
42         }
43         try {
44             latch.await();
45         } catch (InterruptedException e) {
46             log.error("CountDownLatch await wrongly.",e);
47         }
48         log.info("value of parentThreadLocal in parentThread is [{}]",gson.toJson(parentThreadLocal.get()));
49         Map<String,Map<String,Student>> resultMap = Maps.newHashMap();
50         resultMap.put("parentThreadLocal", parentThreadLocal.get());
51         log.info("main thread end..");
52         return resultMap;
53     }
54     
55     private final class SubThread implements Runnable{
56         private ThreadLocal<Map<String,Student>> subThreadLocal;
57         private String identification;
58         private CountDownLatch latch;
59         
60         public SubThread(String identification,CountDownLatch latch){
61             this.identification = identification;
62             subThreadLocal = new ThreadLocal<Map<String,Student>>();
63     /**在這裡設定沒有用,設定的話,父執行緒呼叫該建構函式時,還是在父執行緒執行,
64        subThreadLocal被放置在父執行緒的threadlocals變數,因此在run方法(子執行緒執行)通過get方法獲取執行緒變數中儲存的value是拿不到的
65     **/
66  //           subThreadLocal.set(Maps.newHashMap());   
67             this.latch = latch;
68         }
69 
70         @Override
71         public void run() {
72             log.info("start to execute thread[{}]",identification);
73             subThreadLocal.set(Maps.newHashMap());  //sunThreadLocal 會被放置在子執行緒的threadLocals變數中
74             /***
75              * 輸出的值為null,並不能直接使用父執行緒中定義在parentThreadLocal中的值
76              */
77             log.info("value of parentThreadLocal in subThread is [{}]",gson.toJson(parentThreadLocal.get()));
78             Student subStudent = new Student();
79             subStudent.setName(identification);
80             if(parentThreadLocal.get() != null){
81                 parentThreadLocal.get().put(identification, subStudent);
82             }else {
83                 log.error("cannot set value to parentThreadLocal,because parentThreadLocal has no value");
84             }
85             subThreadLocal.get().put(identification, subStudent);
86             log.info("end:value of parentThreadLocal in subThread is [{}]",gson.toJson(parentThreadLocal.get()));
87             log.info("end:value of subThread in subThread is [{}]",gson.toJson(subThreadLocal.get()));
88             latch.countDown();
89         }
90     }
91 }

執行結果:

2020-07-08T15:01:50.801+08:00 INFO [http-nio-8080-exec-10] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.multi:38] - value of parentThreadLocal in parentThread is [{}]
2020-07-08T15:01:50.803+08:00 INFO [Thread-23] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:72] - start to execute thread[Thread1]
2020-07-08T15:01:50.803+08:00 INFO [Thread-22] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:72] - start to execute thread[Thread0]
2020-07-08T15:01:50.803+08:00 INFO [Thread-22] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:77] - value of parentThreadLocal in subThread is [null]
2020-07-08T15:01:50.803+08:00 INFO [Thread-23] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:77] - value of parentThreadLocal in subThread is [null]
2020-07-08T15:01:50.804+08:00 ERROR [Thread-22] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:83] - cannot set value to parentThreadLocal,because parentThreadLocal has no value
2020-07-08T15:01:50.804+08:00 ERROR [Thread-23] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:83] - cannot set value to parentThreadLocal,because parentThreadLocal has no value
2020-07-08T15:01:50.804+08:00 INFO [Thread-22] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:86] - end:value of parentThreadLocal in subThread is [null]
2020-07-08T15:01:50.804+08:00 INFO [Thread-23] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:86] - end:value of parentThreadLocal in subThread is [null]
2020-07-08T15:01:50.804+08:00 INFO [Thread-24] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:72] - start to execute thread[Thread2]
2020-07-08T15:01:50.804+08:00 INFO [Thread-22] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:87] - end:value of subThread in subThread is [{"Thread0":{"name":"Thread0"}}]
2020-07-08T15:01:50.804+08:00 INFO [Thread-23] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:87] - end:value of subThread in subThread is [{"Thread1":{"name":"Thread1"}}]
2020-07-08T15:01:50.805+08:00 INFO [Thread-24] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:77] - value of parentThreadLocal in subThread is [null]
2020-07-08T15:01:50.806+08:00 ERROR [Thread-24] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:83] - cannot set value to parentThreadLocal,because parentThreadLocal has no value
2020-07-08T15:01:50.807+08:00 INFO [Thread-24] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:86] - end:value of parentThreadLocal in subThread is [null]
2020-07-08T15:01:50.807+08:00 INFO [Thread-24] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.run:87] - end:value of subThread in subThread is [{"Thread2":{"name":"Thread2"}}]
2020-07-08T15:01:50.808+08:00 INFO [http-nio-8080-exec-10] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.multi:48] - value of parentThreadLocal in parentThread is [{}]
2020-07-08T15:01:50.809+08:00 INFO [http-nio-8080-exec-10] [com.example.demo.controller.threadlocalexample.ThreadLocalExample.multi:51] - main thread end..

從上面的執行結果可看出:

父執行緒定義的 ThreadLocal 本地執行緒變數雖然設定了 Map 型別的 value 變數,但是子執行緒在獲取這個 Map 型別 value 的時候獲取不到,無法向其中設定值。

(2) 基本思路

1)父執行緒中定義了 ThreadLocal 執行緒本地變數並通過 set 方法設定了 value 變數,該變數值為一個引用物件; 2)在父執行緒中直接建立子執行緒,子執行緒使用父執行緒定義的 ThreadLocal 執行緒本地變數,通過 get 方法嘗試獲取引用物件,會發現獲取失敗,獲取的始終為 null, 這是因為 父執行緒定義的 ThreadLocal 中變數值不會傳遞到子執行緒中。 3)在通過 ThreadLocal 中的 get/set 方法嘗試獲取/設定值時,都是先從當前執行緒中獲取 threadlocals(Map)物件,從該物件中獲取 ThreadLocal 物件 對應的 value 變數,若 threadlocals 不存在以 ThreadLocal物件 為 key,則設定進去,值預設為 null; (3) 核心程式碼及流程
(4) 結論 1) 每一個threadLocal 執行緒本地變數 都會在被使用的執行緒的 threadLocals 屬性 中,threadLocals 屬性 是 ThreadLocalMap型別,該型別中 有 table屬性,用來 專門儲存 執行緒本地變數; 2) table屬性 是一個數組,陣列中的每一項 形似 key-value ,key為 threadLocal 執行緒本地變數, 值就是該threadLocal物件存放的 value 變數。 從以上分析無論是 get 還是 set 方法 都是從當前執行緒中獲取 threadLocals 物件,然後從 table 屬性中設定/獲取 threadLocal物件 對應的 value 變數。 問題: 現在希望在父執行緒定義一個threadLocal物件,父執行緒建立的所有子執行緒都可以使用父執行緒 threadLocal 中設定的 value 變數(可以是引用物件)。 解決辦法: 在父執行緒中定義 value 對應的全域性引用變數,定義全域性執行緒本地變數,全域性執行緒本地變數中 通過 set() 方法設定了 value 值(全域性引用變數);每個子執行緒在執行的時候都需要存在一段向 全域性執行緒本地變數 threadLocal 設定value 值(全域性引用變數)值的 邏輯 ; 缺點: 1) 該方式過於繁瑣,子執行緒執行的時候,都要在其中給threadLcoal 設定值; 2) 若值是父執行緒動態生成的或一直在變化的,子執行緒在執行過程中無法獲取到; 引申解決辦法: 直接使用 InheriableThreadLocal 2、InheritableThreadLocal InheritableThreadLocal 繼承了 ThreadLocal 物件,其能讓子執行緒在建立的時候自動 繼承父執行緒 中所有執行緒本地變數。 (1) 示例程式碼
  1 package com.example.demo.controller.threadlocalexample;
  2 
  3 import java.util.Map;
  4 import java.util.concurrent.CountDownLatch;
  5 import java.util.concurrent.ExecutorService;
  6 import java.util.concurrent.Executors;
  7 
  8 import org.springframework.web.bind.annotation.GetMapping;
  9 import org.springframework.web.bind.annotation.RequestMapping;
 10 import org.springframework.web.bind.annotation.RestController;
 11 
 12 import com.example.demo.dao.entity.Student;
 13 import com.google.common.collect.Maps;
 14 import com.google.gson.Gson;
 15 
 16 import lombok.extern.slf4j.Slf4j;
 17 
 18 @RestController
 19 @RequestMapping("/inheritablethreadLocal")
 20 @Slf4j
 21 public class InheritableThreadLocalExample {
 22     /**執行緒池***/
 23     private ExecutorService executor;
 24     private Gson gson = new Gson();
 25     /**InheritableThreadLocal 變數****/
 26     private InheritableThreadLocal<Map<String,Student>> parentInheritableThreadLocal;
 27     
 28     public InheritableThreadLocalExample(){
 29         executor = Executors.newFixedThreadPool(2);
 30         parentInheritableThreadLocal = new InheritableThreadLocal<>();
 31     }
 32     /**
 33      * <pre>
 34      * <b>inheritablethreadLocal示例:父執行緒直接建立子執行緒使用,子執行緒使用inheritablethreadLocal中定義的value值</b><br>
 35                *  通過示例可以發現,每個子執行緒都可以使用inheritablethreadLocal中定義的Map變數,並向其中設定資料,所有執行緒執行完畢,也可以在主執行緒中拿到子執行緒設定的資料<br>
 36                 * 原因為:<br>
 37                 *  父執行緒建立子執行緒的時候,會把父執行緒物件中的inheritablethreadLocals變數賦值給子執行緒物件中的inheritablethreadLocals變數中
 38      * </pre>
 39      */
 40     @GetMapping
 41     public Map<String,Map<String,Student>> multi(){
 42         parentInheritableThreadLocal.set(Maps.newConcurrentMap());
 43         log.info("value of parentInheritableThreadLocal in parentThread is [{}]",gson.toJson(parentInheritableThreadLocal.get()));
 44         
 45         CountDownLatch latch = new CountDownLatch(3);
 46         for(int i =0 ;i<=2;i++){
 47             new Thread(new SubThread("Thread"+i,latch)).start();;
 48         }
 49         try {
 50             latch.await();
 51         } catch (InterruptedException e) {
 52             log.error("CountDownLatch await wrongly.",e);
 53         }
 54         log.info("value of parentInheritableThreadLocal in parentThread is [{}]",gson.toJson(parentInheritableThreadLocal.get()));
 55         Map<String,Map<String,Student>> resultMap = Maps.newHashMap();
 56         resultMap.put("parentInheritableThreadLocal", parentInheritableThreadLocal.get());
 57         log.info("main thread end..");
 58         return resultMap;
 59     }
 60     
 61     /**
 62      * <pre>
 63      * <b>inheritablethreadLocal示例:父執行緒通過執行緒池建立子執行緒,子執行緒使用inheritablethreadLocal中定義的value值</b><br>
 64                *  通過示例可以發現,每個子執行緒都可以使用inheritablethreadLocal中定義的Map變數,並向其中設定資料,所有執行緒執行完畢,也可以在主執行緒中拿到子執行緒設定的資料,<br>
 65      *  <br>
 66                 * 原因為:<br>
 67                 *  父執行緒建立子執行緒的時候,會把父執行緒物件中的inheritablethreadLocals變數賦值給子執行緒物件中的inheritablethreadLocals變數中
 68      * </pre>
 69      */
 70     @GetMapping("/pool")
 71     public Map<String,Map<String,Student>> multiWithPool(){
 72         log.info("example of inheritablethreadLocal for subThread from Thread pool...");
 73         parentInheritableThreadLocal.set(Maps.newConcurrentMap());
 74         log.info("value of parentInheritableThreadLocal in parentThread is [{}]",gson.toJson(parentInheritableThreadLocal.get()));
 75         
 76         CountDownLatch latch = new CountDownLatch(3);
 77         for(int i =0 ;i<=2;i++){
 78             executor.submit(new Thread(new SubThread("Thread"+i,latch)));
 79         }
 80         try {
 81             latch.await();
 82         } catch (InterruptedException e) {
 83             log.error("CountDownLatch await wrongly.",e);
 84         }
 85         log.info("value of parentInheritableThreadLocal in parentThread is [{}]",gson.toJson(parentInheritableThreadLocal.get()));
 86         Map<String,Map<String,Student>> resultMap = Maps.newHashMap();
 87         resultMap.put("parentInheritableThreadLocal", parentInheritableThreadLocal.get());
 88         log.info("main thread end..");
 89         return resultMap;
 90     }
 91     
 92     private final class SubThread implements Runnable{
 93         private ThreadLocal<Map<String,Student>> subThreadLocal;
 94         private String identification;
 95         private CountDownLatch latch;
 96         
 97         public SubThread(String identification,CountDownLatch latch){
 98             this.identification = identification;
 99             subThreadLocal = new ThreadLocal<Map<String,Student>>();
100     /**在這裡設定沒有用,設定的話,父執行緒呼叫該建構函式時,還是在父執行緒執行,
101        subThreadLocal被放置在父執行緒的threadlocals變數,因此在run方法(子執行緒執行)通過get方法獲取執行緒變數中儲存的value是拿不到的
102     **/
103  //           subThreadLocal.set(Maps.newHashMap());   
104             this.latch = latch;
105         }
106 
107         @Override
108         public void run() {
109             log.info("start to execute thread[{}]",identification);
110             subThreadLocal.set(Maps.newHashMap());  //sunThreadLocal 會被放置在子執行緒的threadLocals變數中
111             log.info("value of parentInheritableThreadLocal in subThread is [{}]",gson.toJson(parentInheritableThreadLocal.get()));
112             Student subStudent = new Student();
113             subStudent.setName(identification);
114             if(parentInheritableThreadLocal.get() != null){
115                 parentInheritableThreadLocal.get().put(identification, subStudent);
116             }else {
117                 log.error("cannot set value to parentInheritableThreadLocal,because parentInheritableThreadLocal has no value");
118             }
119             subThreadLocal.get().put(identification, subStudent);
120             log.info("end:value of parentInheritableThreadLocal in subThread is [{}]",gson.toJson(parentInheritableThreadLocal.get()));
121             log.info("end:value of subThreadLocal in subThread is [{}]",gson.toJson(subThreadLocal.get()));
122             latch.countDown();
123         }
124     }
125 }

/inheritablethreadLocal 請求 執行結果:

2020-07-08T18:12:22.990+08:00 INFO [http-nio-8080-exec-10] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.multi:43] - value of parentInheritableThreadLocal in parentThread is [{}]
2020-07-08T18:12:22.991+08:00 INFO [Thread-10] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:109] - start to execute thread[Thread0]
2020-07-08T18:12:22.992+08:00 INFO [Thread-10] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:111] - value of parentInheritableThreadLocal in subThread is [{}]
2020-07-08T18:12:22.992+08:00 INFO [Thread-11] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:109] - start to execute thread[Thread1]
2020-07-08T18:12:22.993+08:00 INFO [Thread-12] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:109] - start to execute thread[Thread2]
2020-07-08T18:12:22.993+08:00 INFO [Thread-10] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:120] - end:value of parentInheritableThreadLocal in subThread is [{"Thread0":{"name":"Thread0"}}]
2020-07-08T18:12:22.993+08:00 INFO [Thread-11] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:111] - value of parentInheritableThreadLocal in subThread is [{"Thread0":{"name":"Thread0"}}]
2020-07-08T18:12:22.994+08:00 INFO [Thread-10] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:121] - end:value of subThreadLocal in subThread is [{"Thread0":{"name":"Thread0"}}]
2020-07-08T18:12:22.994+08:00 INFO [Thread-11] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:120] - end:value of parentInheritableThreadLocal in subThread is [{"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T18:12:22.995+08:00 INFO [Thread-12] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:111] - value of parentInheritableThreadLocal in subThread is [{"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T18:12:22.995+08:00 INFO [Thread-11] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:121] - end:value of subThreadLocal in subThread is [{"Thread1":{"name":"Thread1"}}]
2020-07-08T18:12:22.997+08:00 INFO [Thread-12] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:120] - end:value of parentInheritableThreadLocal in subThread is [{"Thread2":{"name":"Thread2"},"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T18:12:22.998+08:00 INFO [Thread-12] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:121] - end:value of subThreadLocal in subThread is [{"Thread2":{"name":"Thread2"}}]
2020-07-08T18:12:22.999+08:00 INFO [http-nio-8080-exec-10] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.multi:54] - value of parentInheritableThreadLocal in parentThread is [{"Thread2":{"name":"Thread2"},"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T18:12:23.001+08:00 INFO [http-nio-8080-exec-10] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.multi:57] - main thread end..

以上請求執行多少次,結果都如上圖所示,是不變的。

/inheritablethreadLocal/pool (使用執行緒池來獲取執行緒)請求執行結果:

服務啟動的時候第一次執行結果:

2020-07-08T18:14:19.101+08:00 INFO [http-nio-8080-exec-1] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.multiWithPool:72] - example of inheritablethreadLocal for subThread from Thread pool...
2020-07-08T18:14:19.102+08:00 INFO [http-nio-8080-exec-1] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.multiWithPool:74] - value of parentInheritableThreadLocal in parentThread is [{}]
2020-07-08T18:14:19.104+08:00 INFO [pool-1-thread-1] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:109] - start to execute thread[Thread0]
2020-07-08T18:14:19.104+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:109] - start to execute thread[Thread1]
2020-07-08T18:14:19.104+08:00 INFO [pool-1-thread-1] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:111] - value of parentInheritableThreadLocal in subThread is [{}]
2020-07-08T18:14:19.104+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:111] - value of parentInheritableThreadLocal in subThread is [{}]
2020-07-08T18:14:19.104+08:00 INFO [pool-1-thread-1] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:120] - end:value of parentInheritableThreadLocal in subThread is [{"Thread0":{"name":"Thread0"}}]
2020-07-08T18:14:19.104+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:120] - end:value of parentInheritableThreadLocal in subThread is [{"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T18:14:19.105+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:121] - end:value of subThreadLocal in subThread is [{"Thread1":{"name":"Thread1"}}]
2020-07-08T18:14:19.105+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:109] - start to execute thread[Thread2]
2020-07-08T18:14:19.104+08:00 INFO [pool-1-thread-1] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:121] - end:value of subThreadLocal in subThread is [{"Thread0":{"name":"Thread0"}}]
2020-07-08T18:14:19.105+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:111] - value of parentInheritableThreadLocal in subThread is [{"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T18:14:19.106+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:120] - end:value of parentInheritableThreadLocal in subThread is [{"Thread2":{"name":"Thread2"},"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T18:14:19.106+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:121] - end:value of subThreadLocal in subThread is [{"Thread2":{"name":"Thread2"}}]
2020-07-08T18:14:19.107+08:00 INFO [http-nio-8080-exec-1] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.multiWithPool:85] - value of parentInheritableThreadLocal in parentThread is [{"Thread2":{"name":"Thread2"},"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T18:14:19.107+08:00 INFO [http-nio-8080-exec-1] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.multiWithPool:88] - main thread end..

第二次執行結果:

2020-07-08T18:20:10.529+08:00 INFO [http-nio-8080-exec-3] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.multiWithPool:72] - example of inheritablethreadLocal for subThread from Thread pool...
2020-07-08T18:20:10.529+08:00 INFO [http-nio-8080-exec-3] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.multiWithPool:74] - value of parentInheritableThreadLocal in parentThread is [{}]
2020-07-08T18:20:10.530+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:109] - start to execute thread[Thread1]
2020-07-08T18:20:10.530+08:00 INFO [pool-1-thread-1] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:109] - start to execute thread[Thread0]
2020-07-08T18:20:10.530+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:111] - value of parentInheritableThreadLocal in subThread is [{"Thread2":{"name":"Thread2"},"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T18:20:10.530+08:00 INFO [pool-1-thread-1] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:111] - value of parentInheritableThreadLocal in subThread is [{"Thread2":{"name":"Thread2"},"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T18:20:10.531+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:120] - end:value of parentInheritableThreadLocal in subThread is [{"Thread2":{"name":"Thread2"},"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T18:20:10.531+08:00 INFO [pool-1-thread-1] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:120] - end:value of parentInheritableThreadLocal in subThread is [{"Thread2":{"name":"Thread2"},"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T18:20:10.531+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:121] - end:value of subThreadLocal in subThread is [{"Thread1":{"name":"Thread1"}}]
2020-07-08T18:20:10.531+08:00 INFO [pool-1-thread-1] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:121] - end:value of subThreadLocal in subThread is [{"Thread0":{"name":"Thread0"}}]
2020-07-08T18:20:10.531+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:109] - start to execute thread[Thread2]
2020-07-08T18:20:10.531+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:111] - value of parentInheritableThreadLocal in subThread is [{"Thread2":{"name":"Thread2"},"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T18:20:10.531+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:120] - end:value of parentInheritableThreadLocal in subThread is [{"Thread2":{"name":"Thread2"},"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T18:20:10.532+08:00 INFO [pool-1-thread-2] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.run:121] - end:value of subThreadLocal in subThread is [{"Thread2":{"name":"Thread2"}}]
2020-07-08T18:20:10.532+08:00 INFO [http-nio-8080-exec-3] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.multiWithPool:85] - value of parentInheritableThreadLocal in parentThread is [{}]
2020-07-08T18:20:10.532+08:00 INFO [http-nio-8080-exec-3] [com.example.demo.controller.threadlocalexample.InheritableThreadLocalExample.multiWithPool:88] - main thread end..

從上面的執行結果可以看出:

1) 在父執行緒中通過建立執行緒的方式使用父執行緒定義的 inheritablethreadLocal 中的value 變數方式,可以達到每次 請求過來的時候,都會通過 inheritablethreadLocal 獲取到各個執行緒執行之後存放的資料。但是該種方式 每次都要建立子執行緒,是對執行緒的濫用,不推薦; 2) 第二種方式是使用了執行緒池,父執行緒中通過執行緒池獲取子執行緒來執行邏輯,在子執行緒中使用 inheritablethreadLocal 中的 value 變數儲存資料。從執行結果看,每次rest請求過來,父執行緒其實都要向 inheritablethreadLocal 中設定新的 value引用變數,而子執行緒是從執行緒池中獲取(重複使用已有執行緒),再加上 父執行緒的 inheritablethreadLocals 變數僅在 子執行緒建立的時候才會繼承,導致 已有子執行緒中的 inheritablethreadLocals 中對應的 inheritablethreadLocal變數是不會更新其儲存的 Value 變數,從而造成了我們上面看到的結果。 (2) 核心程式碼如邏輯

由上圖可看出,InheritableThreadLocal 重新寫了 ThreadLocal 類的 createMap 方法 和 getMap 方法,直接使用 Thread 類中的 inheriableThreadLocals 屬性來儲存 從父執行緒中繼承的 ThreadLocal 物件。 主要流程如下:

  • 在父執行緒中定義全域性執行緒本地變數為 InheritableThreadLocal 型別,父執行緒啟動的時候,通過 InheritableThreadLocal 的 set 方法設定 value變數,該 執行緒本地變數(InheritableThreadLocal)會被放在父執行緒的 inheriableThreadLocals 的 table屬性中,其中 key 為 InheritableThreadLocal物件, value 為 設定的 value變數。
  • 父執行緒建立子執行緒的時候 會自動繼承 父執行緒中的 inheriableThreadLocals ,放在子執行緒的 inheriableThreadLocals (它是一個ThreadLocalMap 型別 )中,見下面的 Thread 類中的紅色區域部分。

Thread 類

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name.toCharArray();

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

ThreadLocal#createInheritedMap 方法的實現 見下面

 static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

ThreadLocal.ThreadLcoalMap#createInheritedMap

/**
         * Construct a new map including all Inheritable ThreadLocals
         * from given parent map. Called only by createInheritedMap.
         *
         * @param parentMap the map associated with parent thread.
         */
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

通過以上方式達到子執行緒自動繼承父執行緒使用的 執行緒本地變數,包括設定執行緒本地變數的 value 變數。

(3)結論

通過以上分析可以看出 建立子執行緒的時候 才會自動繼承 父執行緒中的 執行緒本地變數。但是在實際使用的時候,父執行緒 和 子執行緒 我們都會使用執行緒池來進行管理,若父執行緒每次執行的時候,都會更改 執行緒本地變數 中的 value變數,則該值就不會被從執行緒池中獲取到的執行緒繼承

3、TransmittableThreadLocal 阿里提供了 transmittable-thread-local.jar 包,通過對 InheritableThreadLocal 繼承,利用 設計模式中的 裝飾者模式 對 執行緒中的 executor/executorService、runnable、Callable 進行封裝從而達到父執行緒開啟子執行緒時將父執行緒中的 inheritableThreadLocals 繼承給 要執行的子執行緒(無論該執行緒是從執行緒池複用的還是新建立的)。 (1) 程式碼示例
 1 package com.example.demo.controller.threadlocalexample;
 2 
 3 import java.util.List;
 4 import java.util.Map;
 5 import java.util.concurrent.Callable;
 6 import java.util.concurrent.ExecutionException;
 7 import java.util.concurrent.ExecutorService;
 8 import java.util.concurrent.Executors;
 9 import java.util.concurrent.Future;
10 
11 import org.springframework.web.bind.annotation.GetMapping;
12 import org.springframework.web.bind.annotation.RequestMapping;
13 import org.springframework.web.bind.annotation.RestController;
14 
15 import com.alibaba.ttl.TransmittableThreadLocal;
16 import com.alibaba.ttl.threadpool.TtlExecutors;
17 import com.example.demo.dao.entity.Student;
18 import com.google.common.collect.Lists;
19 import com.google.common.collect.Maps;
20 import com.google.gson.Gson;
21 
22 import lombok.extern.slf4j.Slf4j;
23 
24 /**
25  * 
26  * 
27  *
28  */
29 @RestController
30 @RequestMapping("/transmittableThreadLocal")
31 @Slf4j
32 public class TransmittableThreadLocalExample {
33     /**執行緒池***/
34     private ExecutorService executor;
35     private Gson gson = new Gson();
36     /**TransmittableThreadLocal 變數****/
37     private TransmittableThreadLocal<Map<String,Student>> parentTransmittableThreadLocal;
38     
39     public TransmittableThreadLocalExample(){
40         executor = Executors.newFixedThreadPool(2);
41         executor = TtlExecutors.getTtlExecutorService(executor);
42         parentTransmittableThreadLocal = new TransmittableThreadLocal<>();
43     }
44     
45     @GetMapping("/pool")
46     public Map<String,Map<String,Student>> multiWithPool(){
47         parentTransmittableThreadLocal.set(Maps.newConcurrentMap());
48         log.info("value of parentTransmittableThreadLocal in parentThread is [{}]",gson.toJson(parentTransmittableThreadLocal.get()));
49         List<Future<Void>> threadResults = Lists.newArrayList();
50         for(int i =0 ;i<=2;i++){
51             threadResults.add(executor.submit(new SubThread("Thread"+i)));
52         }
53         
54         for(Future<Void> future:threadResults){
55             try {
56                 future.get();
57             } catch (InterruptedException | ExecutionException e) {
58                 log.error(e.getMessage(),e);
59             }
60         }
61 
62         log.info("value of parentTransmittableThreadLocal in parentThread is [{}]",gson.toJson(parentTransmittableThreadLocal.get()));
63         Map<String,Map<String,Student>> resultMap = Maps.newHashMap();
64         resultMap.put("parentTransmittableThreadLocal", parentTransmittableThreadLocal.get());
65         return resultMap;
66     }
67     
68     private final class SubThread implements Callable<Void>{
69         private TransmittableThreadLocal<Map<String,Student>> subThreadLocal;
70         private String identification;
71         
72         public SubThread(String identification){
73             this.identification = identification;
74             subThreadLocal = new TransmittableThreadLocal<Map<String,Student>>();
75             subThreadLocal.set(Maps.newHashMap());
76         }
77 
78         @Override
79         public Void call() throws Exception {
80             log.info("start to execute thread[{}]",identification);
81             log.info("value of parentTransmittableThreadLocal in subThread is [{}]",gson.toJson(parentTransmittableThreadLocal.get()));
82             Student subStudent = new Student();
83             subStudent.setName(identification);
84             parentTransmittableThreadLocal.get().put(identification, subStudent);
85             subThreadLocal.get().put(identification, subStudent);
86 
87             log.info("end:value of parentTransmittableThreadLocal in subThread is [{}]",gson.toJson(parentTransmittableThreadLocal.get()));
88             log.info("end:value of subThread in subThread is [{}]",gson.toJson(subThreadLocal.get()));
89             return null;
90         }
91     }
92 }

執行結果:

服務啟動的時候第一次執行結果:

2020-07-08T19:02:06.380+08:00 INFO [http-nio-8080-exec-2] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.multiWithPool:48] - value of parentTransmittableThreadLocal in parentThread is [{}]
2020-07-08T19:02:06.390+08:00 INFO [pool-2-thread-1] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:80] - start to execute thread[Thread0]
2020-07-08T19:02:06.390+08:00 INFO [pool-2-thread-2] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:80] - start to execute thread[Thread1]
2020-07-08T19:02:06.390+08:00 INFO [pool-2-thread-1] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:81] - value of parentTransmittableThreadLocal in subThread is [{}]
2020-07-08T19:02:06.390+08:00 INFO [pool-2-thread-2] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:81] - value of parentTransmittableThreadLocal in subThread is [{}]
2020-07-08T19:02:06.393+08:00 INFO [pool-2-thread-2] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:87] - end:value of parentTransmittableThreadLocal in subThread is [{"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T19:02:06.393+08:00 INFO [pool-2-thread-1] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:87] - end:value of parentTransmittableThreadLocal in subThread is [{"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T19:02:06.394+08:00 INFO [pool-2-thread-2] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:88] - end:value of subThread in subThread is [{"Thread1":{"name":"Thread1"}}]
2020-07-08T19:02:06.394+08:00 INFO [pool-2-thread-1] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:88] - end:value of subThread in subThread is [{"Thread0":{"name":"Thread0"}}]
2020-07-08T19:02:06.394+08:00 INFO [pool-2-thread-2] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:80] - start to execute thread[Thread2]
2020-07-08T19:02:06.394+08:00 INFO [pool-2-thread-2] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:81] - value of parentTransmittableThreadLocal in subThread is [{"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T19:02:06.394+08:00 INFO [pool-2-thread-2] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:87] - end:value of parentTransmittableThreadLocal in subThread is [{"Thread2":{"name":"Thread2"},"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T19:02:06.395+08:00 INFO [pool-2-thread-2] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:88] - end:value of subThread in subThread is [{"Thread2":{"name":"Thread2"}}]
2020-07-08T19:02:06.395+08:00 INFO [http-nio-8080-exec-2] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.multiWithPool:62] - value of parentTransmittableThreadLocal in parentThread is [{"Thread2":{"name":"Thread2"},"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]

第二次執行結果:

2020-07-08T19:06:12.590+08:00 INFO [http-nio-8080-exec-5] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.multiWithPool:48] - value of parentTransmittableThreadLocal in parentThread is [{}]
2020-07-08T19:06:12.591+08:00 INFO [pool-2-thread-1] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:80] - start to execute thread[Thread0]
2020-07-08T19:06:12.591+08:00 INFO [pool-2-thread-2] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:80] - start to execute thread[Thread1]
2020-07-08T19:06:12.592+08:00 INFO [pool-2-thread-1] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:81] - value of parentTransmittableThreadLocal in subThread is [{}]
2020-07-08T19:06:12.592+08:00 INFO [pool-2-thread-1] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:87] - end:value of parentTransmittableThreadLocal in subThread is [{"Thread0":{"name":"Thread0"}}]
2020-07-08T19:06:12.592+08:00 INFO [pool-2-thread-2] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:81] - value of parentTransmittableThreadLocal in subThread is [{"Thread0":{"name":"Thread0"}}]
2020-07-08T19:06:12.592+08:00 INFO [pool-2-thread-1] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:88] - end:value of subThread in subThread is [{"Thread0":{"name":"Thread0"}}]
2020-07-08T19:06:12.592+08:00 INFO [pool-2-thread-2] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:87] - end:value of parentTransmittableThreadLocal in subThread is [{"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T19:06:12.592+08:00 INFO [pool-2-thread-1] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:80] - start to execute thread[Thread2]
2020-07-08T19:06:12.593+08:00 INFO [pool-2-thread-1] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:81] - value of parentTransmittableThreadLocal in subThread is [{"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T19:06:12.593+08:00 INFO [pool-2-thread-1] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:87] - end:value of parentTransmittableThreadLocal in subThread is [{"Thread2":{"name":"Thread2"},"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]
2020-07-08T19:06:12.593+08:00 INFO [pool-2-thread-1] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:88] - end:value of subThread in subThread is [{"Thread2":{"name":"Thread2"}}]
2020-07-08T19:06:12.594+08:00 INFO [pool-2-thread-2] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.call:88] - end:value of subThread in subThread is [{"Thread1":{"name":"Thread1"}}]
2020-07-08T19:06:12.595+08:00 INFO [http-nio-8080-exec-5] [com.example.demo.controller.threadlocalexample.TransmittableThreadLocalExample.multiWithPool:62] - value of parentTransmittableThreadLocal in parentThread is [{"Thread2":{"name":"Thread2"},"Thread1":{"name":"Thread1"},"Thread0":{"name":"Thread0"}}]

從上面的執行結果可以看出:

1) 無論執行多少次最終結果都是一樣; 2) 每次 rest 請求通過父執行緒都會 向 transmittableThreadLocal 本地執行緒變數重新設定 Value 引用變數,從執行緒池中獲取執行緒來向 Value引用變數 設定值,都可以設定成功,說明每次在執行前,子執行緒物件中的 inheritableThreadLocals 屬性 中對應的 transmittableThreadLocal變數中存放的就是重新設定的 Value 引用變數; (2) 程式碼結構

說明:

  • TransmittableThreadLocal 繼承了 InheritableThreadLocal 類;
  • 新增靜態變數 或 方法,其中 靜態變數 holder 用來儲存 執行緒中所有 TransmittableThreadLocal 型別的本地執行緒變數 (除了holder本身);靜態方法 doExecuteCallback 在當前 TransmittableThreadLocal 變數的Value值要賦給 子執行緒之後或之前執行(可能是一些特殊邏輯);
  • TransmittableThreadLocal 類中存在 Transmitter 類,該類提供的多個方法 在 將父執行緒中的 TransmittableThreadLocal 本地執行緒變數和 Value值過渡給執行的子執行緒 起到關鍵作用。

說明:

  • 入口主類為 TtlExecutors ,定義的 jdk 提供的 Executor 和 ExecutorService 型別的變數都傳遞給 TtlExecutors 中的方法,方法會對傳遞的變數包裝下生成對應的 ExecutorTtlWrapper 或 ExecutorServiceTtlWrapper 變數;
  • 當程式向 ExecutorTtlWrapper 或 ExecutorServiceTtlWrapper 提交 runnable 或 callable 物件時,裝飾類會 將 runnable 或 callable 物件 轉換成 TtlRunable 或 TtlCallable 物件;
  • 父執行緒的 TransmittableThreadLocal 本地執行緒變數傳遞給 執行中的子執行緒 主要在 執行 TtlRunable 或 TtlCallable 物件 中的 run/call 方法完成;
(3) 主體邏輯

說明:

  • 父執行緒使用 TransmittableThreadLocal 方法中的 get 或 set 方法均會將 TransmittableThreadLocal 本地執行緒變數儲存到 父執行緒中的 inheritableThreadLocals 屬性中的 holder(TransmittableThreadLocal變數)對應的WeakHashMap中,值為null;
  • 對 Java 提供的 ExectorService 做了 Wrapper 封裝;
  • 在執行 分裝後的 ExectorService 物件 的submit方式時,會自動 對 定義的 runnable 或 Callable 物件 Wrapper,而在Wrapper 的同時,就自動 將父執行緒中的 inheritableThreadLocals 屬性中的 holder 中所有 TransmittableThreadLocal變數設定其在父執行緒中的 Vaue 值並存儲在封裝後的 runnable 或 Callable 物件 中;
  • 而執行 分裝後的 runnable 或 Callable 物件 call方法時,會執行向其中增加的邏輯 ---- 從 分裝後的 runnable 或 Callable 物件 中拿取從父執行緒中獲取到的 holder 並全部設定到子執行緒中,這樣子執行緒中與父執行緒 同樣的 transmittableThreadLocal 變數對應 Value 值就是父執行緒中;

通過以上方式 達到子執行緒執行之前自動繼承父執行緒所有本地執行緒變數的 Value 變數。


三、總結 1) ThreadLocal 用來儲存執行緒本地變數,僅是執行緒自己儲存資料的,若一個執行緒要使用另一個執行緒儲存在 ThreadLocal 變數,除非 儲存在 ThreadLocal 中的 Value 變數是執行緒安全的引用變數,且一直引用的是同一個記憶體地址; 2) inheritableThreadLocal 可以讓子執行緒使用父執行緒中存放在 inheritableThreadLocal 的引用變數,但若父執行緒每次執行 都將 引用變數指向的記憶體地址變更且子執行緒是非執行緒池中已有的執行緒,則子執行緒可以使用變更記憶體地址的引用,否則使用的引用變數還是指向舊記憶體地址,造成子執行緒無法使用父執行緒的資料; 3) TransmittableThreadLocal 就可以就覺 2) 的問題,所以當子執行緒使用父執行緒池中的資料,且子執行緒是來自執行緒值的話,建議直接使用 TransmittableThreadLocal。