尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

@Async异步线程:Spring 自带的异步解决方案

@Async异步线程:Spring 自带的异步解决方案
📅 发布时间:2026/7/6 21:46:18

前言
在项目应用中,使用MQ异步调用来实现系统性能优化,完成服务间数据同步是常用的技术手段。如果是在同一台服务器内部,不涉及到分布式系统,单纯的想实现部分业务的异步执行,这里介绍一个更简单的异步方法调用。

对于异步方法调用,从Spring3 开始提供了@Async 注解,该注解可以被标注在方法上,以便异步地调用该方法。调用者将在调用时立即返回,而方法的实际执行将提交给 Spring TaskExecutor 的任务中,由指定的线程池中的线程执行。

正文

一、Spring线程池的分类

以下是官方已经实现的常见的5个TaskExecuter。Spring 宣称对于任何场景,这些TaskExecuter完全够用了:

线程特点
SimpleAsyncTaskExecutor每次请求新开线程,没有最大线程数设置.不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程
SyncTaskExecutor不是异步的线程。同步可以用SyncTaskExecutor,但这个可以说不算一个线程池,因为还在原线程执行。这个类没有实现异步调用,只是一个同步操作。
ConcurrentTaskExecutorExecutor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类。
SimpleThreadPoolTaskExecutor是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类。
ThreadPoolTaskExecutor最常使用,推荐,是阿里巴巴Java开发规范中指定的线程类,要求jdk版本大于等于5。其实质是对java.util.concurrent.ThreadPoolExecutor 的包装。

参考阿里巴巴java开发规范, 在线程池应用中:线程池不允许使用Executors去创建,也不允许使用系统默认的线程池,推荐通过ThreadPoolExecutor的方式,这样的处理方式让开发的工程师更加明确线程池的运行规则,规避资源耗尽的风险。

二、SpringBoot中使用@Async

使用异步线程调用方法,过程如下:

  • 编写配置类,定义线程池
  • 启动类/配置文件上加上注解:@EnableAsync
  • 方法上加上注解:@Async

下面演示案例中,我本地项目的目录,仅供参考:

2.1 启用@Async

关键注解 @EnableAsync !!!可以加载启动类上,也可以加在配置文件上,效果是一样的。

  • 方式一:基于Springboot启动类启用

@EnableAsync
@SpringBootApplication
public class AsyncApplication {

public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}

}

  • 方式二:基于Java配置的启用

// com.example.async.service 为即将开启异步线程业务的包位置(后面有详细讲解)
@EnableAsync
@Configuration
@ComponentScan("com.example.async.service")
public class AsyncConfiguration implements AsyncConfigurer {

@Override
public Executor getAsyncExecutor() {
return executor();
}
...
}

2.2 @Async与线程池

Spring应用默认的线程池,指在@Async注解在使用时,不指定线程池的名称。查看源码,@Async的默认线程池为SimpleAsyncTaskExecutor。

@Slf4j @Service public class BusinessServiceImpl implements BusinessService { /** * 方法4:没有指定线程池,验证默认线程池也ok(不推荐:规避资源耗尽的风险) */ @Async public void asyncDemo4() { log.info("asyncDemo4:" + Thread.currentThread().getName() + " 正在执行 ----------"); try { Thread.sleep(2*1000); } catch (InterruptedException e) { e.printStackTrace(); } log.info("asyncDemo4:" + Thread.currentThread().getName() + " 执行结束!!"); } }

2.3 @Async自定义线程池
自定义线程池,可对系统中线程池更加细粒度的控制,方便调整线程池大小配置,线程执行异常控制和处理。在设置系统自定义线程池代替默认线程池时,虽可通过多种模式设置,但替换默认线程池最终产生的线程池有且只能设置一个(不能设置多个类继承AsyncConfigurer)。

  1. 重新实现接口AsyncConfigurer;
  2. 继承AsyncConfigurerSupport;
  3. 配置由自定义的TaskExecutor替代内置的任务执行器;

三者使用方式大体相同,下面的案例将展示说明其一:实现接口AsyncConfigurer接口的方式。

  • 配置一个线程池 ThreadPoolTaskExecutor

/**
* com.example.async.service:即将开启异步线程的业务方法是哪个
*
* 解释:
* 1.即将开启异步线程业务的包位置:com.example.async.service
* 2.通过 ThreadPoolExecutor 的方式,规避资源耗尽的风险
*/
@EnableAsync
@Configuration
@ComponentScan("com.example.async.service")
public class AsyncConfiguration implements AsyncConfigurer {

@Override
public Executor getAsyncExecutor() {
return executor();
}

/**
* 执行需要依赖线程池,这里就来配置一个线程池
* 1.当池子大小小于corePoolSize,就新建线程,并处理请求
* 2.当池子大小等于corePoolSize,把请求放入workQueue(QueueCapacity)中,池子里的空闲线程就去workQueue中取任务并处理
* 3.当workQueue放不下任务时,就新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize,就用RejectedExecutionHandler来做拒绝处理
* 4.当池子的线程数大于corePoolSize时,多余的线程会等待keepAliveTime长时间,如果无请求可处理就自行销毁
*/
@Bean("asyncExecutor")
public ThreadPoolTaskExecutor executor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//设置线程名
executor.setThreadNamePrefix("async-method-execute-");
//设置核心线程数
executor.setCorePoolSize(10);
//设置最大线程数
executor.setMaxPoolSize(50);
//线程池所使用的缓冲队列
executor.setQueueCapacity(100);
//设置多余线程等待的时间,单位:秒
executor.setKeepAliveSeconds(10);
// 初始化线程
executor.initialize();
return executor;
}
}

  • 执行异步线程方法,指定线程池:value 要与配置类 Bean() 中的name相同

/**
* 异步线程 - 执行业务
* 注意:
* 1.@Async 注解调用用线程池,不指定的话默认:SimpleAsyncTaskExecutor
* 2.SimpleAsyncTaskExecutor 不是真的线程池,这个类不重用线程,默认每次调用都会创建一个新的线程
*/
@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {
/**
* 方法1:@Async 标注为异步任务:执行此方法的时候,会单独开启线程来执行,不影响主线程的执行
*/
@Async("asyncExecutor")
public void asyncDemo1() {
log.info("asyncDemo1:" + Thread.currentThread().getName() + " 正在执行 ----------");
// 故意等10秒,那么异步线程开起来,这样明显看到:方法2不用等方法1执行完就调用了
try {
Thread.sleep(10*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("asyncDemo1:" + Thread.currentThread().getName() + " 执行结束!!");
}

/**
* 方法2:与方法1一起执行,证明2个线程异步执行,互不干扰
*/
@Async("asyncExecutor")
public void asyncDemo2() {
log.info("asyncDemo2:" + Thread.currentThread().getName() + " 正在执行 ----------");
try {
Thread.sleep(5*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("asyncDemo2:" + Thread.currentThread().getName() + " 执行结束!!");
}

/**
* 方法3:没有指定线程池,验证默认线程池也ok(不推荐:规避资源耗尽的风险)
*/
@Async
public void asyncDemo3() {
log.info("asyncDemo3:" + Thread.currentThread().getName() + " 正在执行 ----------");
try {
Thread.sleep(1*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("asyncDemo3:" + Thread.currentThread().getName() + " 执行结束!!");
}
}

2.4 启动测试

通过 AsyncApplication 启动 SpringBoot 项目,Postman 进行接口测试:

http://127.0.0.1:8080/async/demo

  • 我写了4个demo,分别模拟4种情况,详情在注释里有写。

@Slf4j
@RestController
@RequestMapping("/async")
public class AsyncControllor {

@Autowired
private AsyncService asyncMethodService;
@Autowired
private BusinessService businessService;

@GetMapping("/demo")
public String demo() {
log.info("接口调用:【开始】 --------------------");
try {
// 执行异步任务 - 自定义线程池
asyncMethodService.asyncDemo1();
asyncMethodService.asyncDemo2();
asyncMethodService.asyncDemo3();
// 执行异步任务 - 默认线程池
businessService.asyncDemo4();
} catch (Exception e) {
return "Exception";
}
log.info("接口调用:【结束】 --------------------");
return "success";
}
}

  • 运行结果:接口执行结束,异步线程仍在运行

总结

  1. @EnableAsync 是启动 @Async 异步线程的关键,可以加载启动类上,也可以加在配置文件上;
  2. 为了规避资源耗尽的风险,推荐通过ThreadPoolExecutor的方式创建线程池;
  3. @Async 注解标注在方法上,以便异步地调用该方法;

原文链接:https://blog.csdn.net/weixin_44259720/article/details/130292656

相关新闻

  • Wireshark抓包分析MQTT协议:从网络底层透视物联网通信问题
  • STC3115与PIC18LF46K22在电池管理系统中的设计与优化
  • SSRF漏洞攻防全解析:从原理到实战的内网渗透指南

最新新闻

  • MySQL 虚拟列与 JSON 字段:5个步骤构建高效查询索引(避坑 JSON_UNQUOTE)
  • Python Playwright控件定位全解析:从基础选择器到高级场景实战
  • WGCNA vs 基础cor():转录组基因共表达分析的2种R方案性能对比
  • Dlib 68点 vs MediaPipe 478点:人脸关键点检测模型精度与速度实测对比
  • 并行Agent架构解析:从Kimi K2.5 PARL框架看工程落地
  • MySQL 5.6 与 JDBC 8.0 版本兼容性实战:解决 IDEA 连接报错 3 种方案

日新闻

  • AI智能体安全防护框架AgentGuard:从原理到实战部署指南
  • KMX63与PIC18F26K40硬件组合及低功耗设计实践
  • 基于YOLO13改进的门体检测模型:C3k2模块与PoolingFormer技术解析

周新闻

  • 基于YOLOv12的番茄成熟度智能检测系统开发
  • 终极RimWorld模组管理指南:用RimSort告别模组冲突烦恼
  • AI Agent框架开发:从理论到实践的完整指南

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号