
二、深入解析原理
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 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 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..
從上面的執行結果可以看出:

由上圖可看出,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變量,則該值就不會被從線程池中獲取到的線程繼承。
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"}}]
從上面的執行結果可以看出:

說明:
- 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 方法完成;

說明:
- 父線程使用 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 變量。