如何在子线程和线程池中使用 ThreadLocal 传输上下文
2018-09-28 本文已影响464人
_晓__
问题举例
- 在 Spring 框架中,使用 @Async 注解时,如何获取 ThreadLocal 中的数据?
- 使用 CompletableFuture.supplyAsync 处理异步中,supplyAsync执行的方法如何获取 ThreadLocal 中的数据?
- Executor 线程池中,如何获取 ThreadLocal 中的数据?
问题解决
Spring框架 @Async
- 没有配置线程池,每执行一次都会创建新的线程处理,只需要使用 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());
}
}
- 配置线程池,每次执行都会由线程池分配线程,使用 JDK 提供的 InheritableThreadLocal 无法获取到数据,而需要使用 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 配置具体参数
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 不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。
- 如果是当前线程中传输,只需要使用 ThreadLocal 即可。
- TTL 源码解析:https://www.jianshu.com/p/aab6b1e7357d
- TTL GitHub:https://github.com/alibaba/transmittable-thread-local