<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Transmittable ThreadLocal(TTL)支持緩存線程池的 ThreadLocal

          聯(lián)合創(chuàng)作 · 2023-09-29 06:15

          在使用線程池等會池化復(fù)用線程的執(zhí)行組件情況下,提供ThreadLocal值的傳遞功能,解決異步執(zhí)行時上下文傳遞的問題。 一個Java標(biāo)準(zhǔn)庫本應(yīng)為框架/中間件設(shè)施開發(fā)提供的標(biāo)配能力,本庫功能聚焦 & 0依賴,支持Java 13/12/11/10/9/8/7/6。

          JDKInheritableThreadLocal類可以完成父線程到子線程的值傳遞。但對于使用線程池等會池化復(fù)用線程的執(zhí)行組件的情況,線程由線程池創(chuàng)建好,并且線程是池化起來反復(fù)使用的;這時父子線程關(guān)系的ThreadLocal值傳遞已經(jīng)沒有意義,應(yīng)用需要的實際上是把 任務(wù)提交給線程池時ThreadLocal值傳遞到 任務(wù)執(zhí)行時。

          本庫提供的TransmittableThreadLocal類繼承并加強InheritableThreadLocal類,解決上述的問題,使用詳見User Guide。

          整個TTL庫的核心功能(用戶API與框架/中間件的集成API、線程池ExecutorService/ForkJoinPool/TimerTask及其線程工廠的Wrapper),只有不到 1000 SLOC代碼行,非常精小。

          歡迎 ??

          ?? 需求場景

          ThreadLocal的需求場景即是TTL的潛在需求場景,如果你的業(yè)務(wù)需要『在使用線程池等會池化復(fù)用線程的執(zhí)行組件情況下傳遞ThreadLocal』則是TTL目標(biāo)場景。

          下面是幾個典型場景例子。

          1. 分布式跟蹤系統(tǒng)
          2. 日志收集記錄系統(tǒng)上下文
          3. SessionCache
          4. 應(yīng)用容器或上層框架跨應(yīng)用代碼給下層SDK傳遞信息

          各個場景的展開說明參見子文檔 需求場景

          ?? User Guide

          使用類TransmittableThreadLocal來保存值,并跨線程池傳遞。

          TransmittableThreadLocal繼承InheritableThreadLocal,使用方式也類似。

          相比InheritableThreadLocal,添加了

          1. protected方法copy
            用于定制 任務(wù)提交給線程池時 的ThreadLocal值傳遞到 任務(wù)執(zhí)行時 的拷貝行為,缺省傳遞的是引用。
          2. protected方法beforeExecute/afterExecute
            執(zhí)行任務(wù)(Runnable/Callable)的前/后的生命周期回調(diào),缺省是空操作。

          具體使用方式見下面的說明。

          1. 簡單使用

          父線程給子線程傳遞值。

          示例代碼:

          // 在父線程中設(shè)置
          TransmittableThreadLocal<String> context = new TransmittableThreadLocal<String>();
          context.set("value-set-in-parent");
          
          // =====================================================
          
          // 在子線程中可以讀取,值是"value-set-in-parent"
          String value = context.get();

          # 完整可運行的Demo代碼參見SimpleDemo.kt

          這是其實是InheritableThreadLocal的功能,應(yīng)該使用InheritableThreadLocal來完成。

          但對于使用線程池等會池化復(fù)用線程的執(zhí)行組件的情況,線程由線程池創(chuàng)建好,并且線程是池化起來反復(fù)使用的;這時父子線程關(guān)系的ThreadLocal值傳遞已經(jīng)沒有意義,應(yīng)用需要的實際上是把 任務(wù)提交給線程池時ThreadLocal值傳遞到 任務(wù)執(zhí)行時

          解決方法參見下面的這幾種用法。

          2. 保證線程池中傳遞值

          2.1 修飾RunnableCallable

          使用TtlRunnableTtlCallable來修飾傳入線程池的RunnableCallable

          示例代碼:

          TransmittableThreadLocal<String> context = new TransmittableThreadLocal<String>();
          context.set("value-set-in-parent");
          
          Runnable task = new RunnableTask();
          // 額外的處理,生成修飾了的對象ttlRunnable
          Runnable ttlRunnable = TtlRunnable.get(task);
          executorService.submit(ttlRunnable);
          
          // =====================================================
          
          // Task中可以讀取,值是"value-set-in-parent"
          String value = context.get();

          上面演示了Runnable,Callable的處理類似

          TransmittableThreadLocal<String> context = new TransmittableThreadLocal<String>();
          context.set("value-set-in-parent");
          
          Callable call = new CallableTask();
          // 額外的處理,生成修飾了的對象ttlCallable
          Callable ttlCallable = TtlCallable.get(call);
          executorService.submit(ttlCallable);
          
          // =====================================================
          
          // Call中可以讀取,值是"value-set-in-parent"
          String value = context.get();

          # 完整可運行的Demo代碼參見TtlWrapperDemo.kt。

          整個過程的完整時序圖

          時序圖

          2.2 修飾線程池

          省去每次RunnableCallable傳入線程池時的修飾,這個邏輯可以在線程池中完成。

          通過工具類com.alibaba.ttl.threadpool.TtlExecutors完成,有下面的方法:

          • getTtlExecutor:修飾接口Executor
          • getTtlExecutorService:修飾接口ExecutorService
          • getTtlScheduledExecutorService:修飾接口ScheduledExecutorService

          示例代碼:

          ExecutorService executorService = ...
          // 額外的處理,生成修飾了的對象executorService
          executorService = TtlExecutors.getTtlExecutorService(executorService);
          
          TransmittableThreadLocal<String> context = new TransmittableThreadLocal<String>();
          context.set("value-set-in-parent");
          
          Runnable task = new RunnableTask();
          Callable call = new CallableTask();
          executorService.submit(task);
          executorService.submit(call);
          
          // =====================================================
          
          // Task或是Call中可以讀取,值是"value-set-in-parent"
          String value = context.get();

          # 完整可運行的Demo代碼參見TtlExecutorWrapperDemo.kt。

          2.3 使用Java Agent來修飾JDK線程池實現(xiàn)類

          這種方式,實現(xiàn)線程池的傳遞是透明的,業(yè)務(wù)代碼中沒有修飾Runnable或是線程池的代碼。即可以做到應(yīng)用代碼 無侵入。
          # 關(guān)于 無侵入 的更多說明參見文檔Java Agent方式對應(yīng)用代碼無侵入。

          示例代碼:

          // ## 1. 框架上層邏輯,后續(xù)流程框架調(diào)用業(yè)務(wù) ##
          TransmittableThreadLocal<String> context = new TransmittableThreadLocal<String>();
          context.set("value-set-in-parent");
          
          // ## 2. 應(yīng)用邏輯,后續(xù)流程業(yè)務(wù)調(diào)用框架下層邏輯 ##
          ExecutorService executorService = Executors.newFixedThreadPool(3);
          
          Runnable task = new RunnableTask();
          Callable call = new CallableTask();
          executorService.submit(task);
          executorService.submit(call);
          
          // ## 3. 框架下層邏輯 ##
          // Task或是Call中可以讀取,值是"value-set-in-parent"
          String value = context.get();

          Demo參見AgentDemo.kt。執(zhí)行工程下的腳本scripts/run-agent-demo.sh即可運行Demo。

          目前TTL Agent中,修飾了的JDK執(zhí)行器組件(即如線程池)如下:

          1. java.util.concurrent.ThreadPoolExecutor 和 java.util.concurrent.ScheduledThreadPoolExecutor
          2. java.util.concurrent.ForkJoinTask(對應(yīng)的執(zhí)行器組件是java.util.concurrent.ForkJoinPool
            • 修飾實現(xiàn)代碼在TtlForkJoinTransformlet.java。從版本 2.5.1 開始支持。
            • 注意Java 8引入的CompletableFuture與(并行執(zhí)行的)Stream底層是通過ForkJoinPool來執(zhí)行,所以支持ForkJoinPool后,TTL也就透明支持了CompletableFutureStream。??
          3. java.util.TimerTask的子類(對應(yīng)的執(zhí)行器組件是java.util.Timer
            • 修飾實現(xiàn)代碼在TtlTimerTaskTransformlet.java。從版本 2.7.0 開始支持。
            • 注意:從2.11.2版本開始缺省開啟TimerTask的修飾(因為保證正確性是第一位,而不是最佳實踐『不推薦使用TimerTask』:);2.11.1版本及其之前的版本沒有缺省開啟TimerTask的修飾。
            • 使用Agent參數(shù)ttl.agent.enable.timer.task開啟/關(guān)閉TimerTask的修飾:
              • -javaagent:path/to/transmittable-thread-local-2.x.x.jar=ttl.agent.enable.timer.task:true
              • -javaagent:path/to/transmittable-thread-local-2.x.x.jar=ttl.agent.enable.timer.task:false
            • 更多關(guān)于TTL Agent參數(shù)的配置說明詳見TtlAgent.java的JavaDoc。

          關(guān)于java.util.TimerTask/java.util.Timer

          TimerJDK 1.3的老類,不推薦使用Timer類。

          推薦用ScheduledExecutorService。
          ScheduledThreadPoolExecutor實現(xiàn)更強壯,并且功能更豐富。 如支持配置線程池的大?。?code>Timer只有一個線程);TimerRunnable中拋出異常會中止定時執(zhí)行。更多說明參見10. Mandatory Run multiple TimeTask by using ScheduledExecutorService rather than Timer because Timer will kill all running threads in case of failing to catch exceptions. - Alibaba Java Coding Guidelines。

          關(guān)于boot class path設(shè)置

          因為修飾了JDK標(biāo)準(zhǔn)庫的類,標(biāo)準(zhǔn)庫由bootstrap class loader加載;修飾后的JDK類引用了TTL的代碼,所以Java Agent使用方式下TTL Jar文件需要配置到boot class path上。

          TTLv2.6.0開始,加載TTL Agent時會自動設(shè)置TTL Jarboot class path上。
          注意:不能修改從Maven庫下載的TTL Jar文件名(形如transmittable-thread-local-2.x.x.jar)。 如果修改了,則需要自己手動通過-Xbootclasspath JVM參數(shù)來顯式配置(就像TTL之前的版本的做法一樣)。

          自動設(shè)置TTL Jarboot class path的實現(xiàn)是通過指定TTL Java Agent Jar文件里manifest文件(META-INF/MANIFEST.MF)的Boot-Class-Path屬性:

          Boot-Class-Path

          A list of paths to be searched by the bootstrap class loader. Paths represent directories or libraries (commonly referred to as JAR or zip libraries on many platforms). These paths are searched by the bootstrap class loader after the platform specific mechanisms of locating a class have failed. Paths are searched in the order listed.

          更多詳見

          Java的啟動參數(shù)配置

          Java的啟動參數(shù)加上:-javaagent:path/to/transmittable-thread-local-2.x.x.jar。

          如果修改了下載的TTLJar的文件名(transmittable-thread-local-2.x.x.jar),則需要自己手動通過-Xbootclasspath JVM參數(shù)來顯式配置:
          比如修改文件名成ttl-foo-name-changed.jar,則還加上Java的啟動參數(shù):-Xbootclasspath/a:path/to/ttl-foo-name-changed.jar

          Java命令行示例如下:

          java -javaagent:path/to/transmittable-thread-local-2.x.x.jar \
              -cp classes \
              com.alibaba.demo.ttl.agent.AgentDemo

          或是

          # 如果修改了TTL jar文件名 或 TTL版本是 2.6.0 之前,
          # 則還需要顯式設(shè)置 -Xbootclasspath 參數(shù)
          java -javaagent:path/to/ttl-foo-name-changed.jar \
              -Xbootclasspath/a:path/to/ttl-foo-name-changed.jar \
              -cp classes \
              com.alibaba.demo.ttl.agent.AgentDemo

          ?? Java API Docs

          當(dāng)前版本的Java API文檔地址: https://alibaba.github.io/transmittable-thread-local/apidocs/

          ?? Maven依賴

          示例:

          <dependency>
              <groupId>com.alibaba</groupId>
              <artifactId>transmittable-thread-local</artifactId>
              <version>2.11.4</version>
          </dependency>

          可以在 search.maven.org 查看可用的版本。

          ? FAQ

          • Mac OS X下,使用javaagent,可能會報JavaLaunchHelper的出錯信息。
            JDK Bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8021205
            可以換一個版本的JDK。我的開發(fā)機上1.7.0_40有這個問題,1.6.0_51、1.7.0_45可以運行。
            1.7.0_45還是有JavaLaunchHelper的出錯信息,但不影響運行。

          ?? 更多文檔

          ?? 相關(guān)資料

          Jdk Core Classes

          瀏覽 27
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          編輯 分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          編輯 分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色色五月天视频 | 超碰人人爱在线观看 | 天天搞天天干 | 91丨九色丨国产 在线 | 秋霞一区|