問題舉例
- 在 Spring 框架中噩凹,使用 @Async 注解時弟灼,如何獲取 ThreadLocal 中的數(shù)據(jù)显沈?
- 使用 CompletableFuture.supplyAsync 處理異步中锹杈,supplyAsync執(zhí)行的方法如何獲取 ThreadLocal 中的數(shù)據(jù)院崇?
- Executor 線程池中驯鳖,如何獲取 ThreadLocal 中的數(shù)據(jù)闲询?
問題解決
Spring 框架 @Async
- 沒有配置線程池,每執(zhí)行一次都會創(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í)行都會由線程池分配線程记舆,使用 JDK 提供的 InheritableThreadLocal 無法獲取到數(shù)據(jù)鸽捻,而需要使用 Alibaba 擴展 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();
// 重點:使用 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:
- ThreadLocal 不是用來解決對象共享訪問問題的豪筝,而主要是提供了保持對象的方法和避免參數(shù)傳遞的方便的對象訪問方式痰滋。
- 如果是當前線程中傳輸摘能,只需要使用 ThreadLocal 即可。
- TTL 源碼解析:http://www.reibang.com/p/aab6b1e7357d
- TTL GitHub:https://github.com/alibaba/transmittable-thread-local