1. 程式人生 > 實用技巧 >使用 transmittable-thread-local 元件解決 ThreadLocal 父子執行緒資料傳遞問題

使用 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. 個人覺得這裡稱呼"父子執行緒"並不妥當,因為執行緒池是系統啟動之後就已經建立好了的,算了,鑽牛角尖太沒勁了。