使用 transmittable-thread-local 組件解決 ThreadLocal 父子線程數據傳遞問題


在某個項目中,需要使用mybatis-plus多租戶功能以便數據隔離,前端將租戶id傳到后端,后端通過攔截器將該租戶id設置到ThreadLocal以便后續使用,代碼大體上如下所示:

ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>();
threadLocal.set(1);

我在Controller層使用線程池取了租戶id,代碼大體上如下所示:

ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(()->{
	//獲取租戶id
});

這時候出問題了,出現了有時候取得到有時候取不到租戶id的現象,但是經過若干次重試之后就能穩定獲取到租戶id;再次測試則發現如果前端傳了其它的租戶id,后端取得還是上一次獲取到的租戶id,這到底是為啥呢?

問題分析:首先,這里使用了InheritableThreadLocal為的就是實現父子線程傳值,傳了值也能取到,但是也不總是能取到,若干次之后就總是能取到了。看到這種現象,我們正常人的第一反應就是懷疑這里有緩存,每次使用的時候沒有,使用完了就緩存起來,由於線程池在執行任務的時候並非總是使用同一條線程,當線程池中的核心線程全都緩存完了,再請求就穩定不報錯了,然而有緩存的原因所以就算這時候外部請求換了一個租戶id,線程池中的線程仍然使用的是老的租戶id,這也是緩存最直接的體現。。。。。。這里純屬基於現象的個人猜測,並沒有什么實錘,看官們謹慎駕駛,小心翻車。

那怎么解決該問題呢?

該問題產生的原因是InheritableThreadLocal的bug,至於什么bug,我也不清楚(笑),但是有解決方案,解決方案就是使用阿里的transmittable-thread-local 組件,github地址如下:https://github.com/alibaba/transmittable-thread-local 使用起來也非常簡單

首先,引入maven依賴:

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

1. 改變ThreadLocal的創建方式

TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();

// =====================================================

// 在父線程中設置
context.set("value-set-in-parent");

// =====================================================

// 在子線程中可以讀取,值是"value-set-in-parent"
String value = context.get();

2.改變線程池創建方式

ExecutorService executorService = ...
// 額外的處理,生成修飾了的對象executorService
executorService = TtlExecutors.getTtlExecutorService(executorService);

也就是說除了正常創建線程池之外,還要對該線程池做一個代理。

就這么簡單,搞完之后父子線程傳數據就一切正常了。

ps. 個人覺得這里稱呼"父子線程"並不妥當,因為線程池是系統啟動之后就已經創建好了的,算了,鑽牛角尖太沒勁了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM