问题:
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(); } } } } }