1. 当使用ThreadLocal存储数据时,程序中使用了@Async异步任务注解,后导致子任务中无法获取到ThreadLocal存储的数据
2. 使用@Async注解后默认的线程池最大线程数和队列长度为 Integer.MAX_VALUE,当并发量增高后会导致系统负载飙升甚至系统宕机
引入transmittable-thread-local依赖,依据此工具解决子线程获取ThreadLocal存储数据
com.alibaba transmittable-thread-local 2.12.3 compile
public class MyThreadLocal { private static final ThreadLocal
这样就能解决子线程中无法获取父线程中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
...
}
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@NotNull Future submit(Callable 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 threadLocalContext; private final Map mdcContextMap; public Runner(Runnable runnable, Map threadLocalContext, Map 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 implements Callable { private final Thread parentThread; private final Callable runnable; private final Map threadLocalContext; private final Map mdcContextMap; public Caller(Callable runnable, Map threadLocalContext, Map 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(); } } } } }