springboot中@Async做异步操作(Completable异步+ThreadPoolTaskExecutor线程池+@Async注解)
文章目录
- 前置
- 启用异步支持
- 编写异步方法
- 关于线程池(强烈建议自定义线程池!)
- 为什么 @Async 异步未生效?
- 异步方法的异常怎么处理
- 异步方法的返回值
- 使用场景举例子
前置
@Async
用于实现方法级别的异步执行,本质是将方法调用交给后台线程池执行,从而避免阻塞主线程,提升系统的吞吐量和响应速度
强烈建议使用 @Async
时候,需要自定义线程池!
- 如果是一般 java 项目,现在更多是使用 CompletableFuture 配合上 ExecutorService 开启异步操作
- 如果是 java springboot web 项目,更多的是使用 @Async 配合上 ThreadPoolTaskExecutor 配置线程池和结合 CompletableFuture 一起做异步操作
启用异步支持
要使用 @Async,首先需要在 Spring 配置类或启动类上添加 @EnableAsync 注解,以启用异步功能
@EnableAsync // 启用异步支持
编写异步方法
在需要异步执行的方法上添加 @Async 注解,该方法必须是 spring 管理的 bean 中的方法,方法也不能是 final,private 这种
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;@Service
public class EmailService {@Async // 标记为异步方法public void sendEmail(String emailContent) {try {// 模拟发送邮件耗时操作Thread.sleep(2000);System.out.println("邮件发送完成,内容:" + emailContent);} catch (InterruptedException e) {e.printStackTrace();}}
}
关于线程池(强烈建议自定义线程池!)
默认情况下,@Async 使用 Spring 的 SimpleAsyncTaskExecutor 线程池(最大线程数为 Integer.MAX_VALUE),一旦请求激增,有可能会导致 cpu 陡增和内存陡增,导致服务崩溃
强烈建议自定义线程池!
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;@Configuration
@EnableAsync
public class AsyncConfig {@Bean(name = "customTaskExecutor")public ThreadPoolTaskExecutor customTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5); // 核心线程数executor.setMaxPoolSize(10); // 最大线程数executor.setQueueCapacity(100); // 队列容量,避免无边界队列executor.setThreadNamePrefix("Async-"); // 线程名称前缀executor.initialize();return executor;}
}
在 @Async 注解中通过 value 参数指定线程池名称:
@Async("customTaskExecutor") // 使用自定义线程池
public void sendEmail(String emailContent) {// ...
}
为什么 @Async 异步未生效?
- 未启用 @EnableAsync:检查启动类或配置类是否缺少 @EnableAsync
- 如果方法在同一个类中被调用,@Async 会失效(Spring 通过代理实现异步,内部调用不走代理)
- @Async 方法不能是 private 或 final
异步方法的异常怎么处理
异步方法中的异常不会传播到调用方,建议在方法内部捕获并记录
@Async
public void sendEmail(String content) {try {// 业务逻辑} catch (Exception e) {// 记录日志System.err.println("异步任务异常: " + e.getMessage());}
}
异步方法的返回值
@Async 方法可以返回 void 或 Future<T>
Future.get()
能获取结果,但是会阻塞主线程
使用场景举例子
常结合 CompletableFuture 使用
@Async
public CompletableFuture<User> getUserAsync(Long userId) {// 调用用户服务
}@Async
public CompletableFuture<Order> getOrderAsync(Long userId) {// 调用订单服务
}// 聚合结果
public void aggregateData(Long userId) {CompletableFuture<User> userFuture = getUserAsync(userId);CompletableFuture<Order> orderFuture = getOrderAsync(userId);userFuture.thenCombine(orderFuture, (user, order) -> {// 处理聚合结果return new UserProfile(user, order);});
}