SpringBoot整合异步任务
SpringBoot整合异步任务
一、异步任务核心概念
在传统同步编程中,方法调用会阻塞主线程,直到方法执行完成才能继续后续操作。而异步任务通过独立线程执行耗时操作,主线程无需等待,可直接返回结果或执行其他逻辑,显著提升系统吞吐量和接口响应速度。
核心应用场景
- 耗时操作:如文件上传、数据导出、第三方接口调用(短信 / 支付回调)。
- 非实时依赖:如用户注册后异步发送验证邮件,无需等待邮件发送完成即可返回注册成功。
- 批量处理:如异步统计数据、批量推送消息,避免阻塞主线程。
SpringBoot 异步任务核心组件
@Async:标注方法为异步方法,Spring 会自动分配独立线程执行。@EnableAsync:在启动类添加,开启 Spring 异步任务支持。Future:用于接收异步方法的返回值,支持非阻塞获取结果。
二、环境准备
- JDK
- SpringBoot 2.7.x(稳定版,兼容性好)
- 开发工具:IDEA
三、完整实现步骤

1. 项目初始化与依赖配置
(1)创建 SpringBoot 项目
通过Maven创建名SpringBootAsync-Demo为的项目,Group 为 com.yqd。
(2)核心依赖(pom.xml)
SpringBoot 已内置异步任务支持,无需额外依赖,核心依赖如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- SpringBoot父依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
<relativePath/>
</parent>
<groupId>com.yqd</groupId>
<artifactId>SpringBootAsync-Demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringBootAsync-Demo</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Spring Web:提供 HTTP 测试接口 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok:简化实体类和日志代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2. 开启异步任务支持
在项目启动类上添加 @EnableAsync 注解,开启 Spring 异步任务功能:
package com.yqd;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* 异步任务项目启动类
* @EnableAsync:开启 Spring 异步任务支持
*/
@SpringBootApplication
@EnableAsync
public class SpringBootAsyncDemo {
public static void main(String[] args) {
SpringApplication.run(SpringBootAsyncDemo.class, args);
}
}
3. 实现异步任务(无返回值 + 有返回值)
Spring 异步任务支持两种场景:无返回值(如发送短信)和 有返回值(如统计数据),下面分别实现。
(1)异步任务服务类(核心逻辑)
创建 AsyncTaskService 类,通过 @Async 标注异步方法:
package com.yqd.service;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Random;
import java.util.concurrent.Future;
@Service
public class AsyncTaskService {
@Async
public void sendSMS() throws InterruptedException {
System.out.println(LocalDateTime.now().withNano(0) +" 开始调用短信验证码业务方法");
Long startTime = System.currentTimeMillis();
Thread.sleep(5000);
Long endTime = System.currentTimeMillis();
System.out.println(LocalDateTime.now().withNano(0) +" 短信验证码业务方法执行完毕");
System.out.println("执行短信业务总耗时:" + (endTime - startTime)+"毫秒");
}
@Async
public Future<Integer> salesStatistics(String area) throws InterruptedException {
System.out.println(LocalDateTime.now().withNano(0) +" 开始统计【"+area+"】的销售额");
Long startTime = System.currentTimeMillis();
int time=new Random().nextInt(5)+1;
Thread.sleep(time*1000);
Long endTime = System.currentTimeMillis();
System.out.println(LocalDateTime.now().withNano(0) +area+" 地区销售额统计完毕");
int m=time+1000;
System.out.println(area+"销售:"+m+"元,统计总耗时:" + (endTime - startTime)+"毫秒");
return new AsyncResult<Integer>(m);
}
}
4. 编写测试接口(HTTP 触发)
创建控制器 AsyncTaskController,提供接口测试异步任务:
package com.yqd.controller;
import com.yqd.service.AsyncTaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.concurrent.Future;
@RestController
public class AsyncTaskController {
@Autowired
private AsyncTaskService taskService;
@RequestMapping("/sendSMS")
public String sendSMS() throws InterruptedException {
taskService.sendSMS();
return LocalDateTime.now().withNano(0) + " 成功调用短信验证码服务";
}
@RequestMapping("/statistics")
public String salesStatistics() throws Exception {
Long st = System.currentTimeMillis();
//统计北京地区的销售金额
Future<Integer> f1 = taskService.salesStatistics("北京");
//统计上海地区的销售金额
Future<Integer> f2 = taskService.salesStatistics("上海");
//获取统计结果,并累加
Integer result = f1.get()+f2.get();
Long et = System.currentTimeMillis();
System.out.println("统计总计:"+result+"元,控制层调用总耗时:"+(et-st)+"毫秒");
return LocalDateTime.now().withNano(0) + " 成功执行销售金额统计";
}
}
5. 自定义异步线程池(可选,优化性能)
Spring 默认使用 SimpleAsyncTaskExecutor 作为异步线程池,但该线程池无上限,高并发场景可能导致线程过多。建议自定义线程池,控制线程数量和队列大小:
package com.yqd.async.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 自定义异步线程池配置
*/
@Configuration
public class AsyncThreadPoolConfig {
/**
* 核心线程数:线程池维护的最小线程数(默认=CPU核心数)
*/
private static final int CORE_POOL_SIZE = 4;
/**
* 最大线程数:线程池可创建的最大线程数
*/
private static final int MAX_POOL_SIZE = 8;
/**
* 队列容量:任务等待队列大小
*/
private static final int QUEUE_CAPACITY = 100;
/**
* 空闲线程存活时间:核心线程外的线程空闲多久后销毁(秒)
*/
private static final int KEEP_ALIVE_SECONDS = 60;
/**
* 线程池名称前缀:便于日志排查
*/
private static final String THREAD_NAME_PREFIX = "Async-Task-";
@Bean("asyncTaskExecutor") // 自定义线程池 Bean 名称
public Executor asyncTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(QUEUE_CAPACITY);
executor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
// 拒绝策略:当线程池和队列都满时,如何处理新任务
// CallerRunsPolicy:由调用者(主线程)执行,避免任务丢失
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 初始化线程池
executor.initialize();
return executor;
}
}
关联自定义线程池
在 @Async 注解中指定线程池 Bean 名称,使异步任务使用自定义线程池:
// 修改 AsyncTaskService 中的异步方法,指定线程池
@Async("asyncTaskExecutor") // 关联自定义线程池
public void asyncSendSms(String phone) {
// 原有逻辑不变
}
@Async("asyncTaskExecutor")
public Future<Long> asyncStatisticsSales(String area, int delay) {
// 原有逻辑不变
}
四、测试验证(详细实操 + 日志示例)
测试前确保项目已启动,端口默认 8080,通过浏览器、Postman 或 curl 工具发起请求,结合控制台日志验证异步效果。
1.测试无返回值异步任务(sendSMS 接口)
-
预期结果:
a.接口立即响应(耗时≤10ms),返回内容类似:2025-10-29T17:03:05 成功调用短信验证码服务(主线程未阻塞,直接返回)。
b.控制台日志输出顺序(体现异步执行)
2025-10-29T17:25:07 开始调用短信验证码业务方法 2025-10-29T17:25:12 短信验证码业务方法执行完毕 执行短信业务总耗时:5011毫秒 -
核心验证点:接口响应时间远小于异步任务执行时间(5 秒),证明主线程未等待。
2.测试有返回值异步任务(statistics 接口)
-
预期结果:
a.接口响应时间≈最长异步任务耗时(因两个任务并行执行,总耗时≈5 秒,而非 1+5=6 秒),返回内容类似:2025-10-29T17:05:20 成功执行销售金额统计。
b.控制台日志输出(体现并行执行):
2025-10-29T17:25:45 开始统计【上海】的销售额 2025-10-29T17:25:45 开始统计【北京】的销售额 2025-10-29T17:25:46上海 地区销售额统计完毕 上海销售:1001元,统计总耗时:1005毫秒 2025-10-29T17:25:48北京 地区销售额统计完毕 北京销售:1003元,统计总耗时:3005毫秒 统计总计:2004元,控制层调用总耗时:3006毫秒 -
核心验证点:两个异步任务并行执行,总耗时接近最长单个任务耗时,体现异步并行优化效果。
3.验证自定义线程池(可选)
-
验证步骤:
a.确保 AsyncTaskService 中异步方法已指定线程池:@Async("asyncTaskExecutor")。
b.访问任意异步接口(如 /sendSMS),观察控制台日志的线程名。
-
预期结果:日志中线程名以 Async-Task- 为前缀
-
结论:线程名符合自定义配置,说明异步任务已使用自定义线程池(而非 Spring 默认线程池)。
六、总结(核心流程 + 最佳实践)
-
核心整合流程(三步必做)
a.开启支持:启动类添加 @EnableAsync,开启 Spring 异步任务机制。
b.定义异步方法:在 Spring 管理的 Bean 中,用 @Async 标注方法(无返回值→void,有返回值→Future
/CompletableFuture )。 c.触发执行:通过跨 Bean 注入调用异步方法(如 Controller 注入 Service),避免内部调用导致注解失效。
-
最佳实践(提升稳定性 + 性能)
-
必须自定义线程池:默认线程池无上限,高并发易导致 OOM,按 “CPU 密集型 / IO 密集型” 调整参数。
-
异常必须处理:异步任务异常默认静默失败,通过 “局部捕获 + 全局处理” 确保可观测性。
-
避免滥用异步:仅用于 “耗时操作 + 非实时依赖” 场景,简单逻辑同步执行更高效。
-
合理设置超时:有返回值场景用 Future.get(timeout) 避免主线程无限阻塞。
-
监控线程池:通过 Actuator 暴露指标,实时关注活跃线程数、队列长度,避免任务堆积。
- 扩展方向
-
异步任务编排:使用 CompletableFuture 实现更复杂的流程(如并行执行→结果聚合、任务依赖)。
-
任务持久化:结合消息队列(RabbitMQ/Kafka),实现异步任务的重试、幂等性、断点续跑(适用于核心业务)。
-
分布式异步:微服务场景下,使用 Seata/TCC 实现分布式事务,确保异步任务的一致性。

浙公网安备 33010602011771号