問題舉例
- 在 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:
- ThreadLocal 不是用來解決對象共享訪問問題的,而主要是提供了保持對象的方法和避免參數(shù)傳遞的方便的對象訪問方式。
- 如果是當(dāng)前線程中傳輸,只需要使用 ThreadLocal 即可。
- TTL 源碼解析:http://www.itdecent.cn/p/aab6b1e7357d
- TTL GitHub:https://github.com/alibaba/transmittable-thread-local