@Async异步线程:Spring 自带的异步解决方案 📅 2026/7/6 21:31:47 前言在项目应用中使用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启动类启用EnableAsyncSpringBootApplicationpublic class AsyncApplication {public static void main(String[] args) {SpringApplication.run(AsyncApplication.class, args);}}方式二基于Java配置的启用// com.example.async.service 为即将开启异步线程业务的包位置后面有详细讲解EnableAsyncConfigurationComponentScan(com.example.async.service)public class AsyncConfiguration implements AsyncConfigurer {Overridepublic 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。重新实现接口AsyncConfigurer继承AsyncConfigurerSupport配置由自定义的TaskExecutor替代内置的任务执行器三者使用方式大体相同下面的案例将展示说明其一实现接口AsyncConfigurer接口的方式。配置一个线程池 ThreadPoolTaskExecutor/*** com.example.async.service即将开启异步线程的业务方法是哪个** 解释* 1.即将开启异步线程业务的包位置com.example.async.service* 2.通过 ThreadPoolExecutor 的方式规避资源耗尽的风险*/EnableAsyncConfigurationComponentScan(com.example.async.service)public class AsyncConfiguration implements AsyncConfigurer {Overridepublic 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 不是真的线程池这个类不重用线程默认每次调用都会创建一个新的线程*/Slf4jServicepublic class AsyncServiceImpl implements AsyncService {/*** 方法1Async 标注为异步任务执行此方法的时候会单独开启线程来执行不影响主线程的执行*/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不推荐规避资源耗尽的风险*/Asyncpublic 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种情况详情在注释里有写。Slf4jRestControllerRequestMapping(/async)public class AsyncControllor {Autowiredprivate AsyncService asyncMethodService;Autowiredprivate 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;}}运行结果接口执行结束异步线程仍在运行总结EnableAsync 是启动 Async 异步线程的关键可以加载启动类上也可以加在配置文件上为了规避资源耗尽的风险推荐通过ThreadPoolExecutor的方式创建线程池Async 注解标注在方法上以便异步地调用该方法原文链接https://blog.csdn.net/weixin_44259720/article/details/130292656