线程安全反思录(上):ThreadLocal到底安全不?

打印 上一主题 下一主题

主题 978|帖子 978|积分 2934


  

  背景
  之前我有写过关于ThreadLocal的源码解析相关文章:一文带你读懂JDK源码:ThreadLocal类,但其实它不是业务最优办理方案,比如业界有更加先辈的TransmittableThreadLocal可供选择。
  下面我们按脑图来一起琢磨一下
  

  对ThreadLocal的思考

  线程封闭:不同的Thread会指向不同的ThreadLocalMap对象,从而实现了线程封闭;而这个Map的key是ThreadLocal对象,value是需要缓存的值。
  思考一:每个线程都会创建一个ThreadLocalMap,里面是一个Entry数组,会不会浪费内存空间吗?

  答案是不会。
  1.1 内存结构

  

  虽然每个Thread对象都有一个ThreadLocalMap,但这种计划并不是为了每个线程都创建一个全新的Map实例,而是为了实现线程局部变量的存储和访问。
  每个ThreadLocal对象可以被多个线程共享,但在每个线程Thread内部,它们有自己的独立副本,即ThreadLocalMap。
  这种计划允许线程安全地访问和修改数据,而不需要使用同步机制,从而进步了并发性能。
  1.2 数据拓展性

  同时ThreadLocalMap使用数组结构,一个Thread生存了一个ThreadLocalMap,它本质是一个Entry数组,恰好阐明了,一个Thread是可以生存多个ThreadLocal对象值的。
  key则是ThreadLocal.threadLocalHashCode 和 数组长度 举行位与运算的效果,恰好阐明了,一个Thread是可以生存多个ThreadLocal对象值的。
  这很好理解,比如在web背景系统里面,既有生存当前登任命户信息的ThreadLocal,也有生存当前请求trace_id链路标识的ThreadLocal,可能另有生存了租户id的ThreadLocal,它们都是不同的业务数据。
  终极就是,一个Thread的一个Map可以存多个ThreadLocal数据,生存多个范例的数据。
  这很好理解,比如在web背景系统里面,既有生存当前登任命户信息的ThreadLocal,也有生存当前请求trace_id链路标识的ThreadLocal,可能另有生存了租户id的ThreadLocal,它们都是不同的业务数据。
  终极就是,一个Thread的一个Map可以存多个ThreadLocal数据。
  思考二:ThreadLocal无法在父线程和子线程之间透传数据,有更好办法吗?

  答案是有的。
  修改方法也很简单:通过创建 InheritableThreadLocal 替代 ThreadLocal。
  将
  1. private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
复制代码
改为:
  1. private static InheritableThreadLocal inheritableThreadLocal = new InheritableThreadLocal();
复制代码
2.1 测试用例

  1. @Slf4j
  2. public class TestInheritableThreadLocal {
  3.     private static InheritableThreadLocal inheritableThreadLocal = new InheritableThreadLocal();
  4.     public static void main(String[] args) {
  5.         inheritableThreadLocal.set("main");
  6.         log.info("main - threadLocal:{}", inheritableThreadLocal.get());
  7.         new Thread(() -> {
  8.             log.info("main - thread - threadLocal:{}", inheritableThreadLocal.get());
  9.         }).start();
  10.         ThreadPoolExecutor threadPoolExecutor = ThreadPoolUtils.newThreadPool(
  11.                 1, 1,
  12.                 0, TimeUnit.SECONDS,
  13.                 new LinkedBlockingQueue<>(10), null, null);
  14.         threadPoolExecutor.execute(new Runnable() {
  15.             @Override
  16.             public void run() {
  17.                 // 线程池里面的线程
  18.                 log.info("threadPoolExecutor - task1 - threadLocal:{}", inheritableThreadLocal.get());
  19.                 // 线程池里面的线程再new线程
  20.                 new Thread(() -> {
  21.                     log.info("threadPoolExecutor - task1 - new thread - threadLocal:{}", inheritableThreadLocal.get());
  22.                 }).start();
  23.             }
  24.         });
  25.         inheritableThreadLocal.set("main2");
  26.         log.info("main - threadLocal:{}", inheritableThreadLocal.get());
  27.         threadPoolExecutor.execute(new Runnable() {
  28.             @Override
  29.             public void run() {
  30.                 // 线程池里面的线程
  31.                 log.info("threadPoolExecutor - task2 - threadLocal:{}", inheritableThreadLocal.get());
  32.                 // 线程池里面的线程再new线程
  33.                 new Thread(() -> {
  34.                     log.info("threadPoolExecutor - task2 - new thread - threadLocal:{}", inheritableThreadLocal.get());
  35.                 }).start();
  36.             }
  37.         });
  38.     }
  39. }
复制代码
日记

  1. /Library/Java/JavaVirtualMachines/jdk-11.jdk/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA CE.app/Contents/lib/idea_rt.jar=58469:/Applications/IntelliJ IDEA CE.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/bryantmo/Desktop/code/springcloud_test/webdoor/target/test-classes:/Users/bryantmo/Desktop/code/springcloud_test/webdoor/target/classes:/Users/bryantmo/.m2/repository/com/alibaba/transmittable-thread-local/2.12.1/transmittable-thread-local-2.12.1.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-starter-netflix-eureka-client/2.2.6.RELEASE/spring-cloud-starter-netflix-eureka-client-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-starter/2.2.6.RELEASE/spring-cloud-starter-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/security/spring-security-rsa/1.0.9.RELEASE/spring-security-rsa-1.0.9.RELEASE.jar:/Users/bryantmo/.m2/repository/org/bouncycastle/bcpkix-jdk15on/1.64/bcpkix-jdk15on-1.64.jar:/Users/bryantmo/.m2/repository/org/bouncycastle/bcprov-jdk15on/1.64/bcprov-jdk15on-1.64.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-netflix-hystrix/2.2.6.RELEASE/spring-cloud-netflix-hystrix-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-netflix-eureka-client/2.2.6.RELEASE/spring-cloud-netflix-eureka-client-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/com/netflix/eureka/eureka-client/1.10.7/eureka-client-1.10.7.jar:/Users/bryantmo/.m2/repository/com/netflix/netflix-commons/netflix-eventbus/0.3.0/netflix-eventbus-0.3.0.jar:/Users/bryantmo/.m2/repository/com/netflix/netflix-commons/netflix-infix/0.3.0/netflix-infix-0.3.0.jar:/Users/bryantmo/.m2/repository/commons-jxpath/commons-jxpath/1.3/commons-jxpath-1.3.jar:/Users/bryantmo/.m2/repository/joda-time/joda-time/2.10.5/joda-time-2.10.5.jar:/Users/bryantmo/.m2/repository/org/antlr/antlr-runtime/3.4/antlr-runtime-3.4.jar:/Users/bryantmo/.m2/repository/org/antlr/stringtemplate/3.2.1/stringtemplate-3.2.1.jar:/Users/bryantmo/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/Users/bryantmo/.m2/repository/com/google/code/gson/gson/2.9.1/gson-2.9.1.jar:/Users/bryantmo/.m2/repository/org/apache/commons/commons-math/2.2/commons-math-2.2.jar:/Users/bryantmo/.m2/repository/com/netflix/archaius/archaius-core/0.7.6/archaius-core-0.7.6.jar:/Users/bryantmo/.m2/repository/javax/ws/rs/jsr311-api/1.1.1/jsr311-api-1.1.1.jar:/Users/bryantmo/.m2/repository/com/netflix/servo/servo-core/0.12.21/servo-core-0.12.21.jar:/Users/bryantmo/.m2/repository/com/sun/jersey/jersey-core/1.19.1/jersey-core-1.19.1.jar:/Users/bryantmo/.m2/repository/com/sun/jersey/jersey-client/1.19.1/jersey-client-1.19.1.jar:/Users/bryantmo/.m2/repository/com/sun/jersey/contribs/jersey-apache-client4/1.19.1/jersey-apache-client4-1.19.1.jar:/Users/bryantmo/.m2/repository/commons-configuration/commons-configuration/1.10/commons-configuration-1.10.jar:/Users/bryantmo/.m2/repository/com/google/inject/guice/4.1.0/guice-4.1.0.jar:/Users/bryantmo/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/Users/bryantmo/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.10.3/jackson-core-2.10.3.jar:/Users/bryantmo/.m2/repository/org/codehaus/jettison/jettison/1.3.7/jettison-1.3.7.jar:/Users/bryantmo/.m2/repository/stax/stax-api/1.0.1/stax-api-1.0.1.jar:/Users/bryantmo/.m2/repository/com/netflix/eureka/eureka-core/1.10.7/eureka-core-1.10.7.jar:/Users/bryantmo/.m2/repository/com/fasterxml/woodstox/woodstox-core/5.3.0/woodstox-core-5.3.0.jar:/Users/bryantmo/.m2/repository/org/codehaus/woodstox/stax2-api/4.2/stax2-api-4.2.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-starter-netflix-archaius/2.2.6.RELEASE/spring-cloud-starter-netflix-archaius-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-netflix-archaius/2.2.6.RELEASE/spring-cloud-netflix-archaius-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-starter-loadbalancer/2.2.6.RELEASE/spring-cloud-starter-loadbalancer-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-loadbalancer/2.2.6.RELEASE/spring-cloud-loadbalancer-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/io/projectreactor/addons/reactor-extra/3.3.3.RELEASE/reactor-extra-3.3.3.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-starter-cache/2.2.6.RELEASE/spring-boot-starter-cache-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/com/stoyanr/evictor/1.0.0/evictor-1.0.0.jar:/Users/bryantmo/.m2/repository/com/netflix/ribbon/ribbon-eureka/2.3.0/ribbon-eureka-2.3.0.jar:/Users/bryantmo/.m2/repository/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar:/Users/bryantmo/.m2/repository/com/thoughtworks/xstream/xstream/1.4.13/xstream-1.4.13.jar:/Users/bryantmo/.m2/repository/xmlpull/xmlpull/1.1.3.1/xmlpull-1.1.3.1.jar:/Users/bryantmo/.m2/repository/xpp3/xpp3_min/1.1.4c/xpp3_min-1.1.4c.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-config-client/2.2.6.RELEASE/spring-cloud-config-client-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.2.6.RELEASE/spring-boot-autoconfigure-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot/2.2.6.RELEASE/spring-boot-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-commons/2.2.6.RELEASE/spring-cloud-commons-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/security/spring-security-crypto/5.2.2.RELEASE/spring-security-crypto-5.2.2.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-context/2.2.6.RELEASE/spring-cloud-context-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/spring-web/5.2.5.RELEASE/spring-web-5.2.5.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/spring-beans/5.2.5.RELEASE/spring-beans-5.2.5.RELEASE.jar:/Users/bryantmo/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.10.3/jackson-annotations-2.10.3.jar:/Users/bryantmo/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.10.3/jackson-databind-2.10.3.jar:/Users/bryantmo/.m2/repository/org/apache/httpcomponents/httpclient/4.5.12/httpclient-4.5.12.jar:/Users/bryantmo/.m2/repository/org/apache/httpcomponents/httpcore/4.4.13/httpcore-4.4.13.jar:/Users/bryantmo/.m2/repository/commons-codec/commons-codec/1.13/commons-codec-1.13.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-starter-netflix-ribbon/2.2.6.RELEASE/spring-cloud-starter-netflix-ribbon-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-netflix-ribbon/2.2.6.RELEASE/spring-cloud-netflix-ribbon-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/com/netflix/ribbon/ribbon/2.3.0/ribbon-2.3.0.jar:/Users/bryantmo/.m2/repository/com/netflix/ribbon/ribbon-transport/2.3.0/ribbon-transport-2.3.0.jar:/Users/bryantmo/.m2/repository/io/reactivex/rxnetty-contexts/0.4.9/rxnetty-contexts-0.4.9.jar:/Users/bryantmo/.m2/repository/io/reactivex/rxnetty-servo/0.4.9/rxnetty-servo-0.4.9.jar:/Users/bryantmo/.m2/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/Users/bryantmo/.m2/repository/io/reactivex/rxnetty/0.4.9/rxnetty-0.4.9.jar:/Users/bryantmo/.m2/repository/com/netflix/ribbon/ribbon-core/2.3.0/ribbon-core-2.3.0.jar:/Users/bryantmo/.m2/repository/commons-lang/commons-lang/2.6/commons-lang-2.6.jar:/Users/bryantmo/.m2/repository/com/netflix/ribbon/ribbon-httpclient/2.3.0/ribbon-httpclient-2.3.0.jar:/Users/bryantmo/.m2/repository/commons-collections/commons-collections/3.2.2/commons-collections-3.2.2.jar:/Users/bryantmo/.m2/repository/com/netflix/netflix-commons/netflix-commons-util/0.3.0/netflix-commons-util-0.3.0.jar:/Users/bryantmo/.m2/repository/com/netflix/ribbon/ribbon-loadbalancer/2.3.0/ribbon-loadbalancer-2.3.0.jar:/Users/bryantmo/.m2/repository/com/netflix/netflix-commons/netflix-statistics/0.1.1/netflix-statistics-0.1.1.jar:/Users/bryantmo/.m2/repository/io/reactivex/rxjava/1.3.8/rxjava-1.3.8.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-starter-netflix-hystrix/2.2.6.RELEASE/spring-cloud-starter-netflix-hystrix-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/com/netflix/hystrix/hystrix-core/1.5.18/hystrix-core-1.5.18.jar:/Users/bryantmo/.m2/repository/org/hdrhistogram/HdrHistogram/2.1.9/HdrHistogram-2.1.9.jar:/Users/bryantmo/.m2/repository/com/netflix/hystrix/hystrix-serialization/1.5.18/hystrix-serialization-1.5.18.jar:/Users/bryantmo/.m2/repository/com/fasterxml/jackson/module/jackson-module-afterburner/2.10.3/jackson-module-afterburner-2.10.3.jar:/Users/bryantmo/.m2/repository/com/netflix/hystrix/hystrix-metrics-event-stream/1.5.18/hystrix-metrics-event-stream-1.5.18.jar:/Users/bryantmo/.m2/repository/com/netflix/hystrix/hystrix-javanica/1.5.18/hystrix-javanica-1.5.18.jar:/Users/bryantmo/.m2/repository/org/ow2/asm/asm/5.0.4/asm-5.0.4.jar:/Users/bryantmo/.m2/repository/org/aspectj/aspectjweaver/1.9.5/aspectjweaver-1.9.5.jar:/Users/bryantmo/.m2/repository/io/reactivex/rxjava-reactive-streams/1.2.1/rxjava-reactive-streams-1.2.1.jar:/Users/bryantmo/.m2/repository/org/reactivestreams/reactive-streams/1.0.3/reactive-streams-1.0.3.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-starter-actuator/3.3.2/spring-boot-starter-actuator-3.3.2.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-starter/2.2.6.RELEASE/spring-boot-starter-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.2.6.RELEASE/spring-boot-starter-logging-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar:/Users/bryantmo/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar:/Users/bryantmo/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.12.1/log4j-to-slf4j-2.12.1.jar:/Users/bryantmo/.m2/repository/org/apache/logging/log4j/log4j-api/2.12.1/log4j-api-2.12.1.jar:/Users/bryantmo/.m2/repository/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar:/Users/bryantmo/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar:/Users/bryantmo/.m2/repository/org/yaml/snakeyaml/1.25/snakeyaml-1.25.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-actuator-autoconfigure/2.2.6.RELEASE/spring-boot-actuator-autoconfigure-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-actuator/2.2.6.RELEASE/spring-boot-actuator-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/spring-context/5.2.5.RELEASE/spring-context-5.2.5.RELEASE.jar:/Users/bryantmo/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.10.3/jackson-datatype-jsr310-2.10.3.jar:/Users/bryantmo/.m2/repository/io/micrometer/micrometer-observation/1.13.2/micrometer-observation-1.13.2.jar:/Users/bryantmo/.m2/repository/io/micrometer/micrometer-commons/1.13.2/micrometer-commons-1.13.2.jar:/Users/bryantmo/.m2/repository/io/micrometer/micrometer-jakarta9/1.13.2/micrometer-jakarta9-1.13.2.jar:/Users/bryantmo/.m2/repository/io/micrometer/micrometer-core/1.3.6/micrometer-core-1.3.6.jar:/Users/bryantmo/.m2/repository/org/latencyutils/LatencyUtils/2.0.3/LatencyUtils-2.0.3.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-starter-openfeign/2.2.6.RELEASE/spring-cloud-starter-openfeign-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-openfeign-core/2.2.6.RELEASE/spring-cloud-openfeign-core-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/io/github/openfeign/form/feign-form-spring/3.8.0/feign-form-spring-3.8.0.jar:/Users/bryantmo/.m2/repository/io/github/openfeign/form/feign-form/3.8.0/feign-form-3.8.0.jar:/Users/bryantmo/.m2/repository/commons-fileupload/commons-fileupload/1.4/commons-fileupload-1.4.jar:/Users/bryantmo/.m2/repository/commons-io/commons-io/2.2/commons-io-2.2.jar:/Users/bryantmo/.m2/repository/io/github/openfeign/feign-core/10.10.1/feign-core-10.10.1.jar:/Users/bryantmo/.m2/repository/io/github/openfeign/feign-slf4j/10.10.1/feign-slf4j-10.10.1.jar:/Users/bryantmo/.m2/repository/io/github/openfeign/feign-hystrix/10.10.1/feign-hystrix-10.10.1.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.2.6.RELEASE/spring-boot-starter-web-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.2.6.RELEASE/spring-boot-starter-json-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.10.3/jackson-datatype-jdk8-2.10.3.jar:/Users/bryantmo/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.10.3/jackson-module-parameter-names-2.10.3.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.2.6.RELEASE/spring-boot-starter-tomcat-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.33/tomcat-embed-core-9.0.33.jar:/Users/bryantmo/.m2/repository/org/apache/tomcat/embed/tomcat-embed-el/9.0.33/tomcat-embed-el-9.0.33.jar:/Users/bryantmo/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.33/tomcat-embed-websocket-9.0.33.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-starter-validation/2.2.6.RELEASE/spring-boot-starter-validation-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/jakarta/validation/jakarta.validation-api/2.0.2/jakarta.validation-api-2.0.2.jar:/Users/bryantmo/.m2/repository/org/hibernate/validator/hibernate-validator/6.0.18.Final/hibernate-validator-6.0.18.Final.jar:/Users/bryantmo/.m2/repository/org/springframework/spring-webmvc/5.2.5.RELEASE/spring-webmvc-5.2.5.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/spring-aop/5.2.5.RELEASE/spring-aop-5.2.5.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/spring-expression/5.2.5.RELEASE/spring-expression-5.2.5.RELEASE.jar:/Users/bryantmo/Desktop/code/springcloud_test/user-feign-service-api/target/classes:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-starter-data-redis-reactive/3.3.2/spring-boot-starter-data-redis-reactive-3.3.2.jar:/Users/bryantmo/.m2/repository/io/lettuce/lettuce-core/5.2.2.RELEASE/lettuce-core-5.2.2.RELEASE.jar:/Users/bryantmo/.m2/repository/io/netty/netty-common/4.1.48.Final/netty-common-4.1.48.Final.jar:/Users/bryantmo/.m2/repository/io/netty/netty-handler/4.1.48.Final/netty-handler-4.1.48.Final.jar:/Users/bryantmo/.m2/repository/io/netty/netty-resolver/4.1.48.Final/netty-resolver-4.1.48.Final.jar:/Users/bryantmo/.m2/repository/io/netty/netty-buffer/4.1.48.Final/netty-buffer-4.1.48.Final.jar:/Users/bryantmo/.m2/repository/io/netty/netty-codec/4.1.48.Final/netty-codec-4.1.48.Final.jar:/Users/bryantmo/.m2/repository/io/netty/netty-transport/4.1.48.Final/netty-transport-4.1.48.Final.jar:/Users/bryantmo/.m2/repository/io/projectreactor/reactor-core/3.3.4.RELEASE/reactor-core-3.3.4.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/data/spring-data-redis/2.2.6.RELEASE/spring-data-redis-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/data/spring-data-keyvalue/2.2.6.RELEASE/spring-data-keyvalue-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/data/spring-data-commons/2.2.6.RELEASE/spring-data-commons-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/spring-tx/5.2.5.RELEASE/spring-tx-5.2.5.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/spring-oxm/5.2.5.RELEASE/spring-oxm-5.2.5.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/spring-context-support/5.2.5.RELEASE/spring-context-support-5.2.5.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-starter-sleuth/2.2.6.RELEASE/spring-cloud-starter-sleuth-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-starter-aop/2.2.6.RELEASE/spring-boot-starter-aop-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-sleuth-core/2.2.6.RELEASE/spring-cloud-sleuth-core-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/aspectj/aspectjrt/1.9.5/aspectjrt-1.9.5.jar:/Users/bryantmo/.m2/repository/io/zipkin/brave/brave/5.12.7/brave-5.12.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/brave/brave-context-slf4j/5.12.7/brave-context-slf4j-5.12.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/brave/brave-instrumentation-messaging/5.12.7/brave-instrumentation-messaging-5.12.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/brave/brave-instrumentation-rpc/5.12.7/brave-instrumentation-rpc-5.12.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/brave/brave-instrumentation-spring-web/5.12.7/brave-instrumentation-spring-web-5.12.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/brave/brave-instrumentation-http/5.12.7/brave-instrumentation-http-5.12.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/brave/brave-instrumentation-spring-rabbit/5.12.7/brave-instrumentation-spring-rabbit-5.12.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/brave/brave-instrumentation-kafka-clients/5.12.7/brave-instrumentation-kafka-clients-5.12.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/brave/brave-instrumentation-kafka-streams/5.12.7/brave-instrumentation-kafka-streams-5.12.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/brave/brave-instrumentation-httpclient/5.12.7/brave-instrumentation-httpclient-5.12.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/brave/brave-instrumentation-httpasyncclient/5.12.7/brave-instrumentation-httpasyncclient-5.12.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/brave/brave-instrumentation-spring-webmvc/5.12.7/brave-instrumentation-spring-webmvc-5.12.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/brave/brave-instrumentation-servlet/5.12.7/brave-instrumentation-servlet-5.12.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/brave/brave-instrumentation-jms/5.12.7/brave-instrumentation-jms-5.12.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/reporter2/zipkin-reporter-metrics-micrometer/2.15.2/zipkin-reporter-metrics-micrometer-2.15.2.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-starter-zipkin/2.2.6.RELEASE/spring-cloud-starter-zipkin-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/cloud/spring-cloud-sleuth-zipkin/2.2.6.RELEASE/spring-cloud-sleuth-zipkin-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/io/zipkin/zipkin2/zipkin/2.21.7/zipkin-2.21.7.jar:/Users/bryantmo/.m2/repository/io/zipkin/reporter2/zipkin-reporter/2.15.2/zipkin-reporter-2.15.2.jar:/Users/bryantmo/.m2/repository/io/zipkin/reporter2/zipkin-reporter-brave/2.15.2/zipkin-reporter-brave-2.15.2.jar:/Users/bryantmo/.m2/repository/io/zipkin/reporter2/zipkin-sender-kafka/2.15.2/zipkin-sender-kafka-2.15.2.jar:/Users/bryantmo/.m2/repository/io/zipkin/reporter2/zipkin-sender-activemq-client/2.15.2/zipkin-sender-activemq-client-2.15.2.jar:/Users/bryantmo/.m2/repository/io/zipkin/reporter2/zipkin-sender-amqp-client/2.15.2/zipkin-sender-amqp-client-2.15.2.jar:/Users/bryantmo/.m2/repository/org/apache/commons/commons-collections4/4.4/commons-collections4-4.4.jar:/Users/bryantmo/.m2/repository/com/google/guava/guava/30.1.1-jre/guava-30.1.1-jre.jar:/Users/bryantmo/.m2/repository/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar:/Users/bryantmo/.m2/repository/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:/Users/bryantmo/.m2/repository/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar:/Users/bryantmo/.m2/repository/org/checkerframework/checker-qual/3.8.0/checker-qual-3.8.0.jar:/Users/bryantmo/.m2/repository/com/google/errorprone/error_prone_annotations/2.5.1/error_prone_annotations-2.5.1.jar:/Users/bryantmo/.m2/repository/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar:/Users/bryantmo/.m2/repository/javax/servlet/javax.servlet-api/4.0.1/javax.servlet-api-4.0.1.jar:/Users/bryantmo/.m2/repository/org/apache/commons/commons-lang3/3.9/commons-lang3-3.9.jar:/Users/bryantmo/.m2/repository/org/projectlombok/lombok/1.18.18/lombok-1.18.18.jar:/Users/bryantmo/.m2/repository/org/springframework/retry/spring-retry/1.2.5.RELEASE/spring-retry-1.2.5.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/spring-core/5.2.5.RELEASE/spring-core-5.2.5.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/spring-jcl/5.2.5.RELEASE/spring-jcl-5.2.5.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-starter-test/2.2.6.RELEASE/spring-boot-starter-test-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-test/2.2.6.RELEASE/spring-boot-test-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/org/springframework/boot/spring-boot-test-autoconfigure/2.2.6.RELEASE/spring-boot-test-autoconfigure-2.2.6.RELEASE.jar:/Users/bryantmo/.m2/repository/com/jayway/jsonpath/json-path/2.4.0/json-path-2.4.0.jar:/Users/bryantmo/.m2/repository/net/minidev/json-smart/2.3/json-smart-2.3.jar:/Users/bryantmo/.m2/repository/net/minidev/accessors-smart/1.2/accessors-smart-1.2.jar:/Users/bryantmo/.m2/repository/jakarta/xml/bind/jakarta.xml.bind-api/2.3.3/jakarta.xml.bind-api-2.3.3.jar:/Users/bryantmo/.m2/repository/jakarta/activation/jakarta.activation-api/1.2.2/jakarta.activation-api-1.2.2.jar:/Users/bryantmo/.m2/repository/org/junit/vintage/junit-vintage-engine/5.5.2/junit-vintage-engine-5.5.2.jar:/Users/bryantmo/.m2/repository/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar:/Users/bryantmo/.m2/repository/org/junit/platform/junit-platform-engine/1.5.2/junit-platform-engine-1.5.2.jar:/Users/bryantmo/.m2/repository/junit/junit/4.12/junit-4.12.jar:/Users/bryantmo/.m2/repository/org/mockito/mockito-junit-jupiter/3.1.0/mockito-junit-jupiter-3.1.0.jar:/Users/bryantmo/.m2/repository/org/assertj/assertj-core/3.13.2/assertj-core-3.13.2.jar:/Users/bryantmo/.m2/repository/org/hamcrest/hamcrest/2.1/hamcrest-2.1.jar:/Users/bryantmo/.m2/repository/org/mockito/mockito-core/3.1.0/mockito-core-3.1.0.jar:/Users/bryantmo/.m2/repository/net/bytebuddy/byte-buddy/1.10.8/byte-buddy-1.10.8.jar:/Users/bryantmo/.m2/repository/net/bytebuddy/byte-buddy-agent/1.10.8/byte-buddy-agent-1.10.8.jar:/Users/bryantmo/.m2/repository/org/objenesis/objenesis/2.6/objenesis-2.6.jar:/Users/bryantmo/.m2/repository/org/skyscreamer/jsonassert/1.5.0/jsonassert-1.5.0.jar:/Users/bryantmo/.m2/repository/com/vaadin/external/google/android-json/0.0.20131108.vaadin1/android-json-0.0.20131108.vaadin1.jar:/Users/bryantmo/.m2/repository/org/springframework/spring-test/5.2.5.RELEASE/spring-test-5.2.5.RELEASE.jar:/Users/bryantmo/.m2/repository/org/xmlunit/xmlunit-core/2.6.4/xmlunit-core-2.6.4.jar:/Users/bryantmo/.m2/repository/com/google/re2j/re2j/1.3/re2j-1.3.jar:/Users/bryantmo/.m2/repository/org/junit/jupiter/junit-jupiter/5.5.2/junit-jupiter-5.5.2.jar:/Users/bryantmo/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.5.2/junit-jupiter-api-5.5.2.jar:/Users/bryantmo/.m2/repository/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar:/Users/bryantmo/.m2/repository/org/junit/platform/junit-platform-commons/1.5.2/junit-platform-commons-1.5.2.jar:/Users/bryantmo/.m2/repository/org/junit/jupiter/junit-jupiter-params/5.5.2/junit-jupiter-params-5.5.2.jar:/Users/bryantmo/.m2/repository/org/junit/jupiter/junit-jupiter-engine/5.5.2/junit-jupiter-engine-5.5.2.jar:/Users/bryantmo/.m2/repository/org/hibernate/hibernate-validator/5.4.3.Final/hibernate-validator-5.4.3.Final.jar:/Users/bryantmo/.m2/repository/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar:/Users/bryantmo/.m2/repository/org/jboss/logging/jboss-logging/3.4.1.Final/jboss-logging-3.4.1.Final.jar:/Users/bryantmo/.m2/repository/com/fasterxml/classmate/1.5.1/classmate-1.5.1.jar com.bryant.ttl.TestInheritableThreadLocal
  2. 21:04:39.203 [main] INFO com.bryant.ttl.TestInheritableThreadLocal - main - threadLocal:main
  3. 21:04:39.204 [Thread-0] INFO com.bryant.ttl.TestInheritableThreadLocal - main - thread - threadLocal:main
  4. 21:04:39.207 [main] INFO com.bryant.ttl.TestInheritableThreadLocal - main - threadLocal:main2
  5. 21:04:39.207 [pool-1-thread-1] INFO com.bryant.ttl.TestInheritableThreadLocal - threadPoolExecutor - task1 - threadLocal:main
  6. 21:04:39.207 [Thread-1] INFO com.bryant.ttl.TestInheritableThreadLocal - threadPoolExecutor - task1 - new thread - threadLocal:main
  7. 21:04:39.208 [pool-1-thread-1] INFO com.bryant.ttl.TestInheritableThreadLocal - threadPoolExecutor - task2 - threadLocal:main
  8. 21:04:39.208 [Thread-2] INFO com.bryant.ttl.TestInheritableThreadLocal - threadPoolExecutor - task2 - new thread - threadLocal:main
复制代码
分析
  

  • 子线程可以获取父线程的上下文内容
  • 无论是通过ThreadPoolExecutor创建的线程,还是通过new Thread()创建的线程,都可以达到透传数据的目标
  • 但是,线程池执行两个异步线程里,主线程已经对InheritableThreadLocal举行了新的赋值(mian2字符串),但threadPoolExecutor第二次执行任务时,没有准确读入最新的InheritableThreadLocal,而是复用了旧线程的赋值(main字符串)
  2.2 源码:InheritableThreadLocal

  InheritableThreadLocal#set

  1. public void set(T value) {
  2.     Thread t = Thread.currentThread();
  3.     // 【1】
  4.     ThreadLocalMap map = getMap(t);
  5.     if (map != null) {
  6.         map.set(this, value);
  7.     } else {
  8.         // 【2】
  9.         createMap(t, value);
  10.     }
  11. }
  12. ThreadLocalMap getMap(Thread t) {
  13.    return t.inheritableThreadLocals;
  14. }
  15. void createMap(Thread t, T firstValue) {
  16.     t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
  17. }
复制代码
代码分析:
  

  • 【1】getMap(t):如果线程T有inheritableThreadLocals,则返回它
  • 【2】createMap(t, value):如果线程T没有inheritableThreadLocals,new了一个ThreadLocalMap,并赋值线程T的inheritableThreadLocals
  我们观察到Thread除了包含threadLocals,还别的界说了inheritableThreadLocals。
  1. public class Thread implements Runnable {
  2.     ThreadLocal.ThreadLocalMap threadLocals = null;
  3.     ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;   
  4. }
复制代码
InheritableThreadLocal#get

  1. public T get() {
  2.     Thread t = Thread.currentThread();
  3.     // 【3】
  4.     ThreadLocalMap map = getMap(t);
  5.     if (map != null) {
  6.         // 【4】
  7.         ThreadLocalMap.Entry e = map.getEntry(this);
  8.         if (e != null) {
  9.             @SuppressWarnings("unchecked")
  10.             T result = (T)e.value;
  11.             return result;
  12.         }
  13.     }
  14.     // 【5】
  15.     return setInitialValue();
  16. }
  17. ThreadLocalMap getMap(Thread t) {
  18.    return t.inheritableThreadLocals;
  19. }
  20. private T setInitialValue() {
  21.     T value = initialValue();
  22.     Thread t = Thread.currentThread();
  23.     ThreadLocalMap map = getMap(t);
  24.     if (map != null) {
  25.         map.set(this, value);
  26.     } else {
  27.         createMap(t, value);
  28.     }
  29.     if (this instanceof TerminatingThreadLocal) {
  30.         TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
  31.     }
  32.     return value;
  33. }
复制代码
代码分析:
  

  • 【3】获取线程T的inheritableThreadLocals
  • 【4】如果不为空,返回ThreadLocalMap的值
  • 【5】如果为空,初始化ThreadLocalMap,并赋值线程T的inheritableThreadLocals
  Thread的构造器

  1. private Thread(ThreadGroup g, Runnable target, String name,
  2.                long stackSize, AccessControlContext acc,
  3.                boolean inheritThreadLocals) {
  4.     //....
  5.     Thread parent = currentThread();
  6.     // 此处进行inheritThreadLocals的拷贝复制
  7.     if (inheritThreadLocals && parent.inheritableThreadLocals != null)
  8.         this.inheritableThreadLocals =
  9.             ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
  10.     //....
  11. }
复制代码
代码分析:
  

  • 在新建线程时,会拷贝父线程的inheritableThreadLocals,到子线程的inheritableThreadLocals
  思考三:InheritableThreadLocal有透传数据的长处,是否有坑?

  答案是有的。
  比如我们的MVC框架,默认是创建了一个名为“applicationTaskExecutor”线程池,所以一定存在线程复用的场景。
  那么问题就来了。
  如果请求1来了,上下文(用户名=A)放入了InheritableThreadLocal,然后处理掉请求1;
  但由于线程复用,请求1结束处理之后,业务侧忘记清理上下文内存了,或者由于bug出现,漏设置新的上下文了;
  “applicationTaskExecutor”线程池会用这个子线程继续处理请求2,此时的真实上下文是(用户名=B),但子线程拿到的还是(用户名=A);
  这也就是说,出现了内存泄漏。
  办理办法也许多,在业务侧举行计划
  

  • 设置拦截器的preHandler,在一个请求处理前,对InheritableThreadLocal的数据赋值,设置最新的值
  • 设置拦截器的afterHandler,在一个请求处理完后,对InheritableThreadLocal的数据举行remove
  但这个方案仍旧有漏洞,由于业务内部也有线程池,这时候拦截器就无法对内部的线程举行拦截了,所以我们有更好的工具选择:阿里开源的TransmittableThreadLocal。
  思考四:TransmittableThreadLocal的原理是什么?

  

  4.1 测试用例

  1. @Slf4j
  2. public class TestTransmittableThreadLocal {
  3.     private static TransmittableThreadLocal transmittableThreadLocal = new TransmittableThreadLocal();
  4.     public static void main(String[] args) {
  5.         transmittableThreadLocal.set("main");
  6.         log.info("main - threadLocal:{}", transmittableThreadLocal.get());
  7.         new Thread(() -> {
  8.             log.info("thread1 - threadLocal:{}", transmittableThreadLocal.get());
  9.             transmittableThreadLocal.set("thread-1");
  10.             log.info("thread1 - after set - threadLocal:{}", transmittableThreadLocal.get());
  11.         }).start();
  12.         Executor threadPoolExecutor = TtlExecutors.getTtlExecutor(
  13.                 ThreadPoolUtils.newThreadPool(
  14.                 1, 1,
  15.                 0, TimeUnit.SECONDS,
  16.                 new LinkedBlockingQueue<>(10), null, null));
  17.         // 构造一个新的TtlRunnable
  18.         TtlRunnable ttlRunnable = TtlRunnable.get(new Runnable() {
  19.             @Override
  20.             public void run() {
  21.                 // 线程池里面的线程
  22.                 log.info("threadPoolExecutor - task - threadLocal:{}", transmittableThreadLocal.get());
  23.                 // 线程池里面的线程再new线程
  24.                 new Thread(() -> {
  25.                     log.info("threadPoolExecutor - task - thread - threadLocal:{}", transmittableThreadLocal.get());
  26.                 }).start();
  27.             }
  28.         });
  29.         threadPoolExecutor.execute(ttlRunnable);
  30.         transmittableThreadLocal.set("main2");
  31.         log.info("main - threadLocal:{}", transmittableThreadLocal.get());
  32.         // 构造一个新的TtlRunnable
  33.         TtlRunnable ttlRunnable2 =TtlRunnable.get(new Runnable() {
  34.             @Override
  35.             public void run() {
  36.                 // 线程池里面的线程
  37.                 log.info("threadPoolExecutor - task2 - threadLocal:{}", transmittableThreadLocal.get());
  38.                 // 线程池里面的线程再new线程
  39.                 new Thread(() -> {
  40.                     log.info("threadPoolExecutor - task2 - new thread - threadLocal:{}", transmittableThreadLocal.get());
  41.                 }).start();
  42.             }
  43.         });
  44.         threadPoolExecutor.execute(ttlRunnable2);
  45.     }
  46. }
复制代码
日记

  1. 22:01:38.358 [main] INFO com.bryant.ttl.TestTransmittableThreadLocal - main - threadLocal:main
  2. 22:01:38.360 [Thread-0] INFO com.bryant.ttl.TestTransmittableThreadLocal - thread1 - threadLocal:main
  3. 22:01:38.361 [Thread-0] INFO com.bryant.ttl.TestTransmittableThreadLocal - thread1 - after set - threadLocal:thread-1
  4. 22:01:38.363 [main] INFO com.bryant.ttl.TestTransmittableThreadLocal - main - threadLocal:main2
  5. 22:01:38.364 [pool-1-thread-1] INFO com.bryant.ttl.TestTransmittableThreadLocal - threadPoolExecutor - task - threadLocal:main
  6. 22:01:38.364 [pool-1-thread-1] INFO com.bryant.ttl.TestTransmittableThreadLocal - threadPoolExecutor - task2 - threadLocal:main2
  7. 22:01:38.364 [Thread-1] INFO com.bryant.ttl.TestTransmittableThreadLocal - threadPoolExecutor - task - thread - threadLocal:main
  8. 22:01:38.365 [Thread-2] INFO com.bryant.ttl.TestTransmittableThreadLocal - threadPoolExecutor - task2 - new thread - threadLocal:main2
复制代码
分析

  

  • 子线程可以获取父线程的上下文内容
  • 无论是通过ThreadPoolExecutor创建的线程,还是通过new Thread()创建的线程,都可以达到透传数据的目标
  • 对于main线程做的修改,即使是通过线程复用,也能够实时透传给子线程
  4.2 源码:分析TransmittableThreadLocal

  1. private static TransmittableThreadLocal transmittableThreadLocal = new TransmittableThreadLocal();
复制代码
TransmittableThreadLocal#set

  1. private static final InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>> holder =
  2.         new InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>>() {
  3.             @Override
  4.             protected WeakHashMap<TransmittableThreadLocal<Object>, ?>
  5.                 initialValue() {
  6.                 return new WeakHashMap<TransmittableThreadLocal<Object>, Object>();
  7.             }
  8.             @Override
  9.             protected WeakHashMap<TransmittableThreadLocal<Object>, ?>
  10.                 childValue(WeakHashMap<TransmittableThreadLocal<Object>, ?> parentValue) {
  11.                 return new WeakHashMap<TransmittableThreadLocal<Object>, Object>(parentValue);
  12.             }
  13.         };
  14. @Override
  15. public final void set(T value) {
  16.     if (!disableIgnoreNullValueSemantics && null == value) {
  17.         // 【1】
  18.         remove();
  19.     } else {
  20.          // 【2】
  21.         super.set(value);
  22.          // 【3】
  23.         addThisToHolder();
  24.     }
  25. }
  26. @Override
  27. public final void remove() {
  28.     removeThisFromHolder();
  29.     super.remove();
  30. }
  31. private void removeThisFromHolder() {
  32.     holder.get().remove(this);
  33. }
  34. // java.lang.ThreadLocal#remove
  35. public void remove() {
  36.     ThreadLocalMap m = getMap(Thread.currentThread());
  37.     if (m != null) {
  38.         m.remove(this);
  39.     }
  40. }
复制代码
代码分析:
  

  • 【1】若 disableIgnoreNullValueSemantics 为 false 且传入值 value 为 null,则调用 remove() 方法移除值。
  • 【2】否则,调用父类的 set 方法设置值,即,java.lang.ThreadLocal#set
  • 【3】并通过 addThisToHolder 将当前对象添加TransmittableThreadLocal的holder中
  TransmittableThreadLocal#get

  1. @Override
  2. public final T get() {
  3.     T value = super.get();
  4.     if (disableIgnoreNullValueSemantics || null != value) addThisToHolder();
  5.     return value;
  6. }
  7. private void addThisToHolder() {
  8.     if (!holder.get().containsKey(this)) {
  9.         holder.get().put((TransmittableThreadLocal<Object>) this, null); // WeakHashMap supports null value.
  10.     }
  11. }
复制代码
代码分析:
  

  • 从父类获取缓存值 value。
  • 查抄是否禁用忽略空值语义或 value 非空,若是,则将当前对象添加到持有者中。
  • 返回获取的 value。
  TtlExecutors#getTtlExecutor

  1. public final class TtlExecutors {
  2.     @Nullable
  3.     public static Executor getTtlExecutor(@Nullable Executor executor) {
  4.         // 【1】
  5.         if (TtlAgent.isTtlAgentLoaded() || null == executor || executor instanceof TtlEnhanced) {
  6.             return executor;
  7.         }
  8.         // 【2】
  9.         return new ExecutorTtlWrapper(executor, true);
  10.     }
  11. }
  12. class ExecutorTtlWrapper implements Executor, TtlWrapper<Executor>, TtlEnhanced {
  13.     private final Executor executor;
  14.     protected final boolean idempotent;
  15.     //【3】
  16.     ExecutorTtlWrapper(@NonNull Executor executor, boolean idempotent) {
  17.         this.executor = executor;
  18.         this.idempotent = idempotent;
  19.     }
  20. }
复制代码
代码分析:
  

  • 【1】若Ttl代理已加载或传入执行器为null或已是Ttl增强版,则直接返回传入的执行器
  • 【2】否则,返回一个包装后的ExecutorTtlWrapper实例。
  • 【3】构造器会把真正执行任务的线程池,封装起来,而ExecutorTtlWrapper作为一个代理类被调用。
  TtlRunnable#get

  1. public static TtlRunnable get(@Nullable Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
  2.     if (null == runnable) return null;
  3.     if (runnable instanceof TtlEnhanced) {
  4.         // avoid redundant decoration, and ensure idempotency
  5.         if (idempotent) return (TtlRunnable) runnable;
  6.         else throw new IllegalStateException("Already TtlRunnable!");
  7.     }
  8.     return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun);
  9. }
复制代码
代码分析:
  根据给定的参数获取一个TtlRunnable实例:
  

  • 如果传入的runnable为null,返回null。
  • 如果runnable是TtlEnhanced的实例且idempotent为true,直接返回runnable作为TtlRunnable。
  • 否则,创建一个新的TtlRunnable实例并返回。如果runnable已是TtlRunnable实例且idempotent为false,抛出异常。
  焦点:创建一个新的TtlRunnable实例

  Snapshot快照生成

  1. private final AtomicReference<Object> capturedRef;
  2. public final class TtlRunnable implements Runnable, TtlWrapper<Runnable>, TtlEnhanced, TtlAttachments {
  3.     private TtlRunnable(@NonNull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
  4.         //【1】
  5.         this.capturedRef = new AtomicReference<Object>(capture());
  6.         //【2】
  7.         this.runnable = runnable;
  8.         this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
  9.     }
  10. }
  11. public static class Transmitter {
  12.     @NonNull
  13.     public static Object capture() {
  14.         // 【1.1】
  15.         return new Snapshot(captureTtlValues(), captureThreadLocalValues());
  16.     }
  17.     private static HashMap<TransmittableThreadLocal<Object>, Object> captureTtlValues() {
  18.         HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value = new HashMap<TransmittableThreadLocal<Object>, Object>();
  19.         for (TransmittableThreadLocal<Object> threadLocal : holder.get().keySet()) {
  20.             // 【1.2】
  21.             ttl2Value.put(threadLocal, threadLocal.copyValue());
  22.         }
  23.         return ttl2Value;
  24.     }
  25.     private static HashMap<ThreadLocal<Object>, Object> captureThreadLocalValues() {
  26.         final HashMap<ThreadLocal<Object>, Object> threadLocal2Value = new HashMap<ThreadLocal<Object>, Object>();
  27.         for (Map.Entry<ThreadLocal<Object>, TtlCopier<Object>> entry : threadLocalHolder.entrySet()) {
  28.             final ThreadLocal<Object> threadLocal = entry.getKey();
  29.             final TtlCopier<Object> copier = entry.getValue();
  30.             // 【1.3】
  31.             threadLocal2Value.put(threadLocal, copier.copy(threadLocal.get()));
  32.         }
  33.         return threadLocal2Value;
  34.     }
  35. }
  36. private static class Snapshot {
  37.     final HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value;
  38.     final HashMap<ThreadLocal<Object>, Object> threadLocal2Value;
  39.     private Snapshot(HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value, HashMap<ThreadLocal<Object>, Object> threadLocal2Value) {
  40.         this.ttl2Value = ttl2Value;
  41.         this.threadLocal2Value = threadLocal2Value;
  42.     }
  43. }
复制代码
代码分析:
  

  • 【1】capturedRef方法取到快照对象Snapshot,赋值给AtomicReference原子类

    • captureTtlValues():Snapshot的ttl2Value,数据泉源于holder的注册值
    • captureThreadLocalValues():Snapshot的threadLocal2Value,数据泉源于threadLocalHolder的注册值
    • capture():构造了Snapshot对象

  • 【2】任务缓存起来
  通过上面的源码分析,我们可以得到下面的流程图:
  只要异步线程执行前,调用了TransmittableThreadLocal#set,那么就会触发addThisToHolder对holder的TransmittableThreadLocal举行数据更新
  

  Snapshot快照重放

  1. public final class TtlRunnable implements Runnable, TtlWrapper<Runnable>, TtlEnhanced, TtlAttachments {
  2.     @Override
  3.     public void run() {
  4.          // 【1】
  5.         final Object captured = capturedRef.get();
  6.         if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
  7.             throw new IllegalStateException("TTL value reference is released after run!");
  8.         }
  9.         //【2】
  10.         final Object backup = replay(captured);
  11.         try {
  12.             // 【3】
  13.             runnable.run();
  14.         } finally {
  15.             // 【4】
  16.             restore(backup);
  17.         }
  18.     }
  19.     @NonNull
  20.     public static Object replay(@NonNull Object captured) {
  21.         final Snapshot capturedSnapshot = (Snapshot) captured;
  22.         return new Snapshot(replayTtlValues(capturedSnapshot.ttl2Value), replayThreadLocalValues(capturedSnapshot.threadLocal2Value));
  23.     }
  24.     public static void restore(@NonNull Object backup) {
  25.         final Snapshot backupSnapshot = (Snapshot) backup;
  26.         restoreTtlValues(backupSnapshot.ttl2Value);
  27.         restoreThreadLocalValues(backupSnapshot.threadLocal2Value);
  28.     }
  29. }
复制代码
代码分析:
  

  • 线程池执行TtlRunnable任务时:

    • holder值恢复
    • threadLocalHolder值恢复
    • 【1】任务执行前 - 获取快照Snapshot1
    • 【2】任务执行前 - 重新构造一个新快照对象Snapshot2
    • 【3】任务执行 runnable.run();
    • 【4】任务执行后 - 重放Snapshot2

  通过上面的源码分析,我们可以得到下面的流程图:
  确保了线程复用时,能够获取到线程执行前最新的数据,而不用担心上一次执行后留下的脏数据效果
  

  
ExecutorTtlWrapper#execute:封装好的TTL线程池执行任务

  1. class ExecutorTtlWrapper implements Executor, TtlWrapper<Executor>, TtlEnhanced {
  2.     private final Executor executor;
  3.     @Override
  4.     public void execute(@NonNull Runnable command) {
  5.         executor.execute(TtlRunnable.get(command, false, idempotent));
  6.     }
  7. }
复制代码
代码分析:
  

  • TtlRunnable.get:参考上面的 TtlRunnable#get
  • executor.execute:封装好的线程池,执行任务即可
  思考五:三种工具对比

  
长处
缺点
ThreadLocal
简单使用,每个线程都有独立的变量副本,避免了线程安全问题。
不能跨线程传递变量值,不适用于需要在线程间共享数据的场景。
InheritableThreadLocal
允许父子线程间传递变量值,适用于需要传递上下文信息的场景。
不适用于线程池的线程复用场景,由于线程池中的线程可能不是父子关系。
别的,InheritableThreadLocal的传递是发生在新建线程的时候,但是在我们项目中基本都是用线程池来使用多线程,由于线程的创建销毁都会有资源消耗,当然也有可能发生oom
TransmittableThreadLocal
支持在线程池等场景中传递变量值,适用于需要跨多个线程共享数据的场景
相比ThreadLocal和InheritableThreadLocal,实现更复杂,使用时需要额外注意线程安全
  拓展:SpringMVC异步线程池去接每个请求的,可能出现的内存泄漏?

  由于SpringBoot应用,会通过@SpringBootApplication来自动加载相关系统设置类,所以我们了解一下加载类,先看spring.factories:
  

  里面有一个类:
  1. org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
复制代码
TaskExecutionAutoConfiguration
  1. @ConditionalOnClass(ThreadPoolTaskExecutor.class)
  2. @Configuration(proxyBeanMethods = false)
  3. @EnableConfigurationProperties(TaskExecutionProperties.class)
  4. public class TaskExecutionAutoConfiguration {
  5.    /**
  6.     * Bean name of the application {@link TaskExecutor}.
  7.     */
  8.    public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";
  9.    @Bean
  10.    @ConditionalOnMissingBean
  11.    public TaskExecutorBuilder taskExecutorBuilder(TaskExecutionProperties properties,
  12.          ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers,
  13.          ObjectProvider<TaskDecorator> taskDecorator) {
  14.       TaskExecutionProperties.Pool pool = properties.getPool();
  15.       TaskExecutorBuilder builder = new TaskExecutorBuilder();
  16.       builder = builder.queueCapacity(pool.getQueueCapacity());
  17.       builder = builder.corePoolSize(pool.getCoreSize());
  18.       builder = builder.maxPoolSize(pool.getMaxSize());
  19.       builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());
  20.       builder = builder.keepAlive(pool.getKeepAlive());
  21.       Shutdown shutdown = properties.getShutdown();
  22.       builder = builder.awaitTermination(shutdown.isAwaitTermination());
  23.       builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());
  24.       builder = builder.threadNamePrefix(properties.getThreadNamePrefix());
  25.       builder = builder.customizers(taskExecutorCustomizers.orderedStream()::iterator);
  26.       builder = builder.taskDecorator(taskDecorator.getIfUnique());
  27.       return builder;
  28.    }
  29.    @Lazy
  30.    @Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,
  31.          AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
  32.    @ConditionalOnMissingBean(Executor.class)
  33.    public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
  34.       return builder.build();
  35.    }
  36. }
复制代码
代码分析:
  

  • 这个设置类,欺压要求有ThreadPoolTaskExecutor才可以初始化
  • TaskExecutionProperties:spring.task.execution开头的设置项,我们可以基于此举行定制化
  • 通过建造者模式,使用TaskExecutorBuilder举行的ThreadPoolTaskExecutor封装构建。
  其他文章

  深刻理解Redis集群(上):RDB快照和AOF日记
  深刻理解Redis集群(中):Redis主从数据同步模式
  深刻理解Redis集群(下):Redis 哨兵(Sentinel)模式
  Kafka消息堆积问题排查
  基于SpringMVC的API灰度方案
  理解到位:灾备和只读数据库
  SQL管理经验谈:索引覆盖
  Mybatis链路分析:JDK动态代理和责任链模式的应用
  大模子安装摆设、测试、接入SpringCloud应用体系
  Mybatis插件-租户ID的注入&拦截应用

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

滴水恩情

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表