如何在子线程和线程池中使用 ThreadLocal 传输上下文

问题举例

  • 在 Spring 框架中,使用 @Async 注解时,如何获取 ThreadLocal 中的数据?
  • 使用 CompletableFuture.supplyAsync 处理异步中,supplyAsync执行的方法如何获取 ThreadLocal 中的数据?
  • Executor 线程池中,如何获取 ThreadLocal 中的数据?

问题解决

Spring 框架 @Async

  • 没有配置线程池,每执行一次都会创建新的线程处理,只需要使用 InheritableThreadLocal 即可获取。
public final class ThreadContext {
    private static final ThreadLocal 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 配置:


  com.alibaba
  transmittable-thread-local


这只是配置线程池方式之一,这里不阐述太多,只是举例说明使用:

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 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
对代码不明白地方可联系讨论或对写的不好的地方望不吝指教。

你可能感兴趣的:(如何在子线程和线程池中使用 ThreadLocal 传输上下文)