zoukankan      html  css  js  c++  java
  • 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。
  • 相关阅读:
    DateTime.Now.ToString("yyyy/MM/dd") 时间格式化中的MM为什么是大写的?
    新入门PGSQL数据库(尝试利用PGPOOL实现分布式),摘录笔记
    MongoDB入门教程之C#驱动操作实例
    使用MongoDB C#官方驱动操作MongoDB
    【OOAD】OOAD概述
    【OOAD】设计模式概述
    【OOAD】面向对象设计原则概述
    【OOAD】OOP的主要特征
    深入浅出设计模式——访问者模式(Visitor Pattern)
    深入浅出设计模式——模板方法模式(Template Method Pattern)
  • 原文地址:https://www.cnblogs.com/sandyflower/p/12932296.html
Copyright © 2011-2022 走看看