一、为什么需要异步线程池
在 Java 里,异步执行本质就是把任务提交给线程池执行,而不是阻塞在当前线程。
-
场景:IO 请求(HTTP、RPC、DB)、消息消费、批量计算。
-
目标:减少主线程阻塞、提升吞吐量、平滑限流。
核心类就是 ThreadPoolExecutor 最佳实践-优快云博客 中介绍的 Executor、ExecutorService、ThreadPoolExecutor。
Spring 在此之上封装,减少配置复杂度 + 统一上下文传播。
二、Spring 异步线程池入门
1. 开启异步支持
@SpringBootApplication
@EnableAsync // 开启异步支持
public class DemoApplication {}
2. 定义异步线程池
Spring 推荐使用 ThreadPoolTaskExecutor(包装了 JDK 的 ThreadPoolExecutor,额外支持 Spring 生命周期管理、监控指标)。
@Configuration
public class AsyncConfig {
@Bean("asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5); // 核心线程数
executor.setMaxPoolSize(10); // 最大线程数
executor.setQueueCapacity(100); // 队列容量
executor.setKeepAliveSeconds(60); // 空闲线程存活时间
executor.setThreadNamePrefix("async-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
3. 使用异步方法
@Service
public class MyService {
@Async("asyncExecutor") // 指定线程池
public CompletableFuture<String> doWork() throws InterruptedException {
Thread.sleep(2000);
return CompletableFuture.completedFuture("done");
}
}
4. 异步返回值类型
-
void→ 仅执行,不关心结果 -
Future<T>→ JDK 传统异步返回 -
CompletableFuture<T>→ 支持链式编排,更现代(推荐)
四、源码分析
1. @EnableAsync
-
入口:
@EnableAsync -
导入类:
AsyncConfigurationSelector -
它会注册:
-
AsyncAnnotationBeanPostProcessor→ 核心,扫描@Async方法 -
AsyncExecutionInterceptor→ 方法调用拦截器
-
2. Bean 后处理:AsyncAnnotationBeanPostProcessor
-
继承
AbstractBeanFactoryAwareAdvisingPostProcessor -
作用:扫描带
@Async注解的方法,给对应的 bean 生成代理(JDK 动态代理或 CGLIB) -
代理会织入一个 Advisor,指向
AsyncExecutionInterceptor
public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor
implements BeanFactoryAware, BeanClassLoaderAware, Ordered {
// 核心逻辑在 postProcessAfterInitialization()
// 为带 @Async 的 bean 生成代理
}
3. 拦截器:AsyncExecutionInterceptor
-
继承
AsyncExecutionAspectSupport -
核心方法
invoke(…):-
拿到原始方法的
MethodInvocation -
封装成一个
Callable或Runnable -
提交给
Executor(默认SimpleAsyncTaskExecutor,可通过配置改为自定义线程池) -
处理返回值:如果返回
Future/CompletableFuture,会自动封装;否则返回空
-
关键片段(简化):
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Callable<Object> task = () -> invocation.proceed();
Future<Object> result = this.executor.submit(task);
return adaptToReturnType(result, invocation.getMethod());
}
4. 默认 Executor 的选择
-
如果用户没配线程池,Spring 会用:
-
SimpleAsyncTaskExecutor(每次新建线程,没有复用)
-
-
如果实现了
AsyncConfigurer,则用你提供的Executor -
如果用
@Async("beanName"),会从容器里找对应的线程池 bean
五、最佳实践
1. 线程池定制(推荐)
@Bean("asyncExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("async-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
2. 使用 CompletableFuture
比 Future 更现代,支持链式编排、组合任务。
@Async("asyncExecutor")
public CompletableFuture<String> asyncJob() {
return CompletableFuture.completedFuture("OK");
}
3. 异常处理
全局处理未捕获的异常:
@Configuration
public class AsyncExceptionConfig implements AsyncConfigurer {
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, params) -> log.error("Async error", ex);
}
}
4. 上下文传递(日志 TraceId / 用户信息)
默认不会传递 ThreadLocal,需要 TaskDecorator:
executor.setTaskDecorator(r -> {
Map<String, String> context = MDC.getCopyOfContextMap();
return () -> {
MDC.setContextMap(context);
try { r.run(); } finally { MDC.clear(); }
};
});
复杂情况可以用阿里巴巴的 TransmittableThreadLocal(TTL)。
5. 优雅停机
应用关停时等待异步任务完成:
executor.setWaitForTasksToCompleteOnShutdown(true); executor.setAwaitTerminationSeconds(30);
6. 多线程池隔离
不同业务独立线程池,避免“日志任务堵死业务任务”这种悲剧。
六、常见误区
-
调用自己类的 @Async 方法无效
因为代理没生效,自调用绕过了 AOP。 -
返回值不是 Future/CompletableFuture
如果是void,异常不会抛到调用方,只能通过AsyncUncaughtExceptionHandler捕获。 -
上下文丢失
日志 traceId、SecurityContext 这种 ThreadLocal 不会自动传递。 -
滥用异步
异步适合 IO 密集,CPU 密集不建议大量开线程,容易竞争 CPU。

被折叠的 条评论
为什么被折叠?



