(一) Spring @Async
注解底层异步线程池原理
Spring 的 @Async
注解通过 异步线程池 实现非阻塞调用,其底层原理主要依赖 代理模式 和 线程池管理。以下是详细解析:
1. 核心实现原理
- 代理模式:
Spring 通过ProxyFactory
为标注@Async
的方法生成代理对象,代理对象拦截方法调用并提交到异步线程池执行。 - 线程池管理:
异步任务的实际执行由 Spring 管理的线程池完成(默认或自定义配置)。
2. 关键组件与流程
(1) 启用异步支持
- 在配置类上添加
@EnableAsync
注解,触发 Spring 扫描并注册异步相关 Bean:@Configuration @EnableAsync public class AsyncConfig { // 可自定义线程池配置(见下文) }
(2) 代理对象生成
- Spring AOP 为
@Async
方法生成代理对象(JDK 动态代理或 CGLIB),代理逻辑如下:- 拦截方法调用。
- 将方法调用封装为
Runnable
或Callable
任务。 - 提交任务到异步线程池执行。
(3) 线程池执行任务
- 默认线程池(
SimpleAsyncTaskExecutor
):- 每次调用创建新线程(无复用),适合简单场景但性能差。
- 自定义线程池(推荐):
- 通过实现
AsyncConfigurer
接口或配置TaskExecutor
Bean 指定线程池。
- 通过实现
(4) 异步任务执行
- 线程池从池中获取线程执行任务,主线程继续处理后续逻辑(非阻塞)。
3. 默认线程池 vs 自定义线程池
特性 | 默认线程池 (SimpleAsyncTaskExecutor ) | 自定义线程池 (ThreadPoolTaskExecutor ) |
---|---|---|
线程复用 | 不复用,每次创建新线程 | 复用线程池中的线程 |
性能 | 差(高并发时资源耗尽) | 好(可控的线程数) |
适用场景 | 测试或简单场景 | 生产环境推荐 |
配置方式 | 无需配置(自动使用) | 需显式配置 TaskExecutor Bean |
4. 自定义线程池配置示例
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
// 创建线程池
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5); // 核心线程数
executor.setMaxPoolSize(10); // 最大线程数
executor.setQueueCapacity(100); // 队列容量
executor.setThreadNamePrefix("Async-"); // 线程名前缀
executor.initialize(); // 初始化线程池
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
// 异步异常处理(可选)
return (ex, method, params) -> {
System.err.println("异步方法 " + method.getName() + " 执行异常: " + ex.getMessage());
};
}
}
5. 关键注意事项
- 线程池隔离:
- 不同业务场景可配置多个线程池(通过
@Async("customExecutor")
指定)。
- 不同业务场景可配置多个线程池(通过
- 异常处理:
- 默认异常会打印日志,可通过
AsyncUncaughtExceptionHandler
自定义处理。
- 默认异常会打印日志,可通过
- 事务失效:
- 异步方法默认不在原事务上下文中执行(需显式开启新事务)。
- 返回值:
- 异步方法不能直接返回值(除非使用
Future
或CompletableFuture
)。
- 异步方法不能直接返回值(除非使用
6. 底层线程池实现类
Spring 默认使用的线程池实现类:
SimpleAsyncTaskExecutor
(默认,不推荐生产使用)。ThreadPoolTaskExecutor
(推荐,基于java.util.concurrent.ThreadPoolExecutor
)。
总结
@Async
的异步能力本质是通过 代理拦截方法调用 + 线程池执行任务 实现的。生产环境应避免使用默认线程池,而是通过 ThreadPoolTaskExecutor
自定义线程池配置,以平衡性能与资源利用率。
(二) Spring 异步线程池任务拒绝的处理机制
当 Spring 异步线程池的任务队列已满且线程数达到最大值时,新提交的任务会被拒绝。Spring 提供了多种处理策略,具体取决于使用的线程池实现类(如 ThreadPoolTaskExecutor
)。
1. 默认拒绝策略(AbortPolicy
)
- 行为:直接抛出
RejectedExecutionException
异常。 - 适用场景:需要明确感知任务被拒绝的情况(如监控或告警)。
- 示例:
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); // 默认策略
2. 其他内置拒绝策略
Spring 的 ThreadPoolTaskExecutor
底层基于 Java 的 ThreadPoolExecutor
,支持以下拒绝策略:
策略类 | 行为描述 |
---|---|
CallerRunsPolicy | 由提交任务的线程直接执行该任务(阻塞提交线程,适用于降级处理)。 |
DiscardPolicy | 直接丢弃任务,不抛出异常(静默失败,适用于非关键任务)。 |
DiscardOldestPolicy | 丢弃队列中最旧的任务(即最早提交但未执行的任务),然后尝试重新提交新任务。 |
配置示例:
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 提交线程执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); // 静默丢弃
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); // 丢弃最旧任务
3. 自定义拒绝策略
通过实现 RejectedExecutionHandler
接口,可以自定义任务拒绝时的处理逻辑:
public class MyRejectedHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 自定义逻辑:记录日志、发送告警、降级处理等
System.err.println("任务被拒绝: " + r.toString());
// 可选:将任务存入数据库或消息队列稍后重试
}
}
// 配置到线程池
executor.setRejectedExecutionHandler(new MyRejectedHandler());
4. 关键注意事项
- 策略选择原则:
- 高可靠性场景:使用
CallerRunsPolicy
或自定义策略(确保任务不丢失)。 - 非关键任务:使用
DiscardPolicy
或DiscardOldestPolicy
(避免阻塞系统)。
- 高可靠性场景:使用
- 监控与告警:
- 建议对任务拒绝事件进行监控(如通过日志或指标系统),及时发现线程池过载问题。
- 线程池调优:
- 优先通过调整
corePoolSize
、maxPoolSize
和queueCapacity
减少拒绝概率,而非依赖拒绝策略。
- 优先通过调整
5. 完整配置示例
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("Async-");
// 设置拒绝策略(这里使用CallerRunsPolicy)
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
总结
Spring 异步线程池的任务拒绝处理通过 RejectedExecutionHandler
实现,常见策略包括:
- 默认:抛出异常(
AbortPolicy
)。 - 降级:提交线程执行(
CallerRunsPolicy
)。 - 丢弃:静默丢弃(
DiscardPolicy
)或丢弃最旧任务(DiscardOldestPolicy
)。 - 自定义:通过实现接口灵活处理。
生产环境中应根据业务重要性选择策略,并结合线程池调优减少拒绝概率。