如何在子線程和線程池中使用 ThreadLocal 傳輸上下文

問題舉例

  • 在 Spring 框架中,使用 @Async 注解時(shí),如何獲取 ThreadLocal 中的數(shù)據(jù)?
  • 使用 CompletableFuture.supplyAsync 處理異步中,supplyAsync執(zhí)行的方法如何獲取 ThreadLocal 中的數(shù)據(jù)?
  • Executor 線程池中,如何獲取 ThreadLocal 中的數(shù)據(jù)?

問題解決

Spring 框架 @Async

  • 沒有配置線程池,每執(zhí)行一次都會(huì)創(chuàng)建新的線程處理,只需要使用 InheritableThreadLocal 即可獲取。
public final class ThreadContext {
    private static final ThreadLocal<Long> USER_ID_LOCAL = new InheritableThreadLocal<>();

    public static Long getUserId() {
        return USER_ID_LOCAL.get();
    }

    public static void setUserId(Long userId) {
        USER_ID_LOCAL.set(userId);
    }

    public static void clear() {
        USER_ID_LOCAL.remove();
    }
}

public class BusinessTask {

    @Async
    public void handle() {
        System.out.println(ThreadContext.getUserId());
    }
}
  • 配置線程池,每次執(zhí)行都會(huì)由線程池分配線程,使用 JDK 提供的 InheritableThreadLocal 無法獲取到數(shù)據(jù),而需要使用 Alibaba 擴(kuò)展 InheritableThreadLocal 的 TransmittableThreadLocal。

maven pom 配置:

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

這只是配置線程池方式之一,這里不闡述太多,只是舉例說明使用:

public class AsyncThreadPoolConfiguration implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();
        // TODO 配置具體參數(shù)
        threadPool.initialize();

        // 重點(diǎn):使用 TTL 提供的 TtlExecutors
        return TtlExecutors.getTtlExecutor(threadPool);
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }
}

public final class ThreadContext {
    // 只需替換 InheritableThreadLocal 為 TransmittableThreadLocal 即可
    private static final ThreadLocal<Long> USER_ID_LOCAL = new TransmittableThreadLocal<>();

    public static Long getUserId() {
        return USER_ID_LOCAL.get();
    }

    public static void setUserId(Long userId) {
        USER_ID_LOCAL.set(userId);
    }

    public static void clear() {
        USER_ID_LOCAL.remove();
    }
}

public class BusinessTask {

    @Async
    public void handle() {
        System.out.println(ThreadContext.getUserId());
    }
}
  • 把 InheritableThreadLocal 替換為 TTL 提供的 TransmittableThreadLocal
  • 使用 TTL 提供的 TtlExecutors 包裝線程池對象

通過解決了 Spring @Async 注解的問題,即可舉一反三,CompletableFuture.supplyAsync 和 Executor 亦可以在這兩種方法處理。
線程池中傳輸必須配合 TransmittableThreadLocal 和 TtlExecutors 使用。

PS:
對代碼不明白地方可聯(lián)系討論或?qū)懙牟缓玫牡胤酵涣咧附獭?/h5>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容