使用Async如何传递ThreadLocal及自定义线程池

文章讨论了在使用Spring的@Async注解进行异步任务处理时,ThreadLocal和MDC数据无法在子线程中正确传递的问题。提出了两种解决方案:一是引入阿里巴巴的TransmittableThreadLocal库来传递ThreadLocal数据;二是自定义线程池,通过扩展ThreadPoolTaskExecutor并重写execute和submit方法,手动在子线程中复制ThreadLocal和MDC上下文。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题:

        1. 当使用ThreadLocal存储数据时,程序中使用了@Async异步任务注解,后导致子任务中无法获取到ThreadLocal存储的数据

        2. 使用@Async注解后默认的线程池最大线程数和队列长度为 Integer.MAX_VALUE,当并发量增高后会导致系统负载飙升甚至系统宕机

方案一:

引入transmittable-thread-local依赖,依据此工具解决子线程获取ThreadLocal存储数据

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>transmittable-thread-local</artifactId>
  <version>2.12.3</version>
  <scope>compile</scope>
</dependency>
public class MyThreadLocal {

  private static final ThreadLocal<Map<String, Object>> param = new TransmittableThreadLocal<>();
...
}

这样就能解决子线程中无法获取父线程中ThreadLocal值的问题,使用方法简单,改造工作量很小,然后创建自定义线程池

@Configuration
public class AsyncThreadPoolConfig implements AsyncConfigurer {

  private static final String CUSTOMIZE_THREAD_POOL_PREFIX = "custom_thread_pool_";

  @Override
//  @Bean(name = "a1") 如果要定义多个,在@Async("a1")注解上表明使用哪个就可以了
  public Executor getAsyncExecutor() {
    ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();

    threadPoolTaskExecutor.setCorePoolSize(2);
    threadPoolTaskExecutor.setMaxPoolSize(100);
    threadPoolTaskExecutor.setThreadNamePrefix(CUSTOMIZE_THREAD_POOL_PREFIX);
    threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
    threadPoolTaskExecutor.setQueueCapacity(200);
    threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    threadPoolTaskExecutor.initialize();

    return  TtlExecutors.getTtlExecutor(threadPoolTaskExecutor);
  }

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

但是这个方案还是没办法解决MDC传递的问题

方案二:

不需要引入其他包,自定义线程池,实现ThreadLocal和MDC数据的传递

public class MyThreadLocal { private static final ThreadLocal<Map<String, Object>> param = new ThreadLocal<>();

...
}

import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.slf4j.MDC;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
public class AsyncThreadPoolConfig implements AsyncConfigurer {

  private static final String CUSTOMIZE_THREAD_POOL_PREFIX = "custom_thread_pool_";

  @Override
// @Bean(name = "a1") 如果要定义多个,在@Async("a1")注解上表明使用哪个就可以了 
  public Executor getAsyncExecutor() {
    ThreadPoolTaskExecutor threadPoolTaskExecutor = new CustomThreadPoolTaskExecutor();

    threadPoolTaskExecutor.setCorePoolSize(2);
    threadPoolTaskExecutor.setMaxPoolSize(100);
    threadPoolTaskExecutor.setThreadNamePrefix(CUSTOMIZE_THREAD_POOL_PREFIX);
    threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
    threadPoolTaskExecutor.setQueueCapacity(200);
    threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    threadPoolTaskExecutor.initialize();

    return threadPoolTaskExecutor;
  }

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

  private static class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {

    @Override
    public void execute(Runnable task) {
      super.execute(new AsyncThreadPoolConfig.Runner(task, MyThreadLocal.getParam(),
          MDC.getCopyOfContextMap(), Thread.currentThread()));
    }

    @Override
    public Future<?> submit(Runnable task) {
      return super.submit(new AsyncThreadPoolConfig.Runner(task, MyThreadLocal.getParam(),
          MDC.getCopyOfContextMap(), Thread.currentThread()));
    }

    @Override
    public <T> @NotNull Future<T> submit(Callable<T> task) {
      return super.submit(new AsyncThreadPoolConfig.Caller(task, MyThreadLocal.getParam(),
          MDC.getCopyOfContextMap(), Thread.currentThread()));
    }
  }

  @Slf4j
  private static class Runner implements Runnable {

    private final Thread parentThread;
    private final Runnable runnable;
    private final Map<String, Object> threadLocalContext;
    private final Map<String, String> mdcContextMap;

    public Runner(Runnable runnable, Map<String, Object> threadLocalContext,
        Map<String, String> mdcContextMap, Thread parentThread) {
      this.runnable = runnable;
      this.mdcContextMap = mdcContextMap;
      this.threadLocalContext = threadLocalContext;
      this.parentThread = parentThread;
    }

    @Override
    public void run() {
      //这里是子线程
      MyThreadLocal.setParam(threadLocalContext);
      MDC.setContextMap(mdcContextMap);
      try {
        runnable.run();
      } catch (Exception e) {
        log.error("异步任务异常 {}", e.getMessage());
        throw e;
      } finally {
        if (parentThread != Thread.currentThread()) {
          MyThreadLocal.removeParam();
          MDC.clear();
        }
      }
    }
  }

  @Slf4j
  private static class Caller<V> implements Callable<V> {

    private final Thread parentThread;
    private final Callable<V> runnable;
    private final Map<String, Object> threadLocalContext;
    private final Map<String, String> mdcContextMap;

    public Caller(Callable<V> runnable, Map<String, Object> threadLocalContext,
        Map<String, String> mdcContextMap, Thread parentThread) {
      this.runnable = runnable;
      this.threadLocalContext = threadLocalContext;
      this.mdcContextMap = mdcContextMap;
      this.parentThread = parentThread;
    }

    @Override
    public V call() throws Exception {
      //这里是子线程
      MyThreadLocal.setParam(threadLocalContext);
      MDC.setContextMap(mdcContextMap);
      try {
        return runnable.call();
      } catch (Exception e) {
        log.error("异步任务异常 {}", e.getMessage());
        throw e;
      } finally {
        if (parentThread != Thread.currentThread()) {
          MyThreadLocal.removeParam();
          MDC.clear();
        }
      }
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值