Spring中@Async用法总结
简介
在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在spring 3.x之后,就已经内置了@Async来完美解决这个问题,本文将完成介绍@Async的用法。
使用场景
1. 何为异步调用?
在解释异步调用之前,我们先来看同步调用的定义;同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果。 异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕;而是继续执行下面的流程。
例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法;如他们都是同步调用,则需要将他们都顺序执行完毕之后,方算作过程执行完毕; 如B为一个异步的调用方法,则在执行完A之后,调用B,并不等待B完成,而是执行开始调用C,待C执行完毕之后,就意味着这个过程执行完毕了。
2. 常规的异步调用处理方式
在Java中,一般在处理类似的场景之时,都是基于创建独立的线程去完成相应的异步调用逻辑,通过主线程和不同的线程之间的执行流程,从而在启动独立的线程之后,主线程继续执行而不会产生停滞等待的情况。
3. @Async介绍
在Spring中,基于@Async标注的方法,称之为异步方法;这些方法将在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。
启用方式
如何在Spring中启用@Async
基于Java配置的启用方式:
@Configuration @EnableAsync public class SpringAsyncConfig { ... }
基于XML配置文件的启用方式,配置如下:
<task:executor id="myexecutor" pool-size="5" /> <task:annotation-driven executor="myexecutor"/>
配置线程池
package cn.huanzi.qch.springbootasync.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; /** * 线程池的配置 */ @Configuration public class AsyncConfig { private static final int MAX_POOL_SIZE = 50; private static final int CORE_POOL_SIZE = 20; @Bean("asyncTaskExecutor") public AsyncTaskExecutor asyncTaskExecutor() { ThreadPoolTaskExecutor asyncTaskExecutor = new ThreadPoolTaskExecutor(); asyncTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE); asyncTaskExecutor.setCorePoolSize(CORE_POOL_SIZE); asyncTaskExecutor.setThreadNamePrefix("async-task-thread-pool-"); asyncTaskExecutor.initialize(); return asyncTaskExecutor; } }
使用方法
直接使用线程池
@Autowired AsyncTaskExecutor asyncTaskExecutor;//注入线程池对象 //通过线程池对象提交异步任务 asyncTaskExecutor.submit(() -> { log.info("异步任务开始"); //省略异步任务业务逻辑... log.info("异步任务结束"); });
无返回值
@Async("asyncTaskExecutor") public void asyncTask() { long startTime = System.currentTimeMillis(); try { //模拟耗时 Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + ":void asyncTask(),耗时:" + (endTime - startTime)); }
有返回值
@Async("asyncTaskExecutor")public Future<String> asyncTask(String s) { long startTime = System.currentTimeMillis(); try { //模拟耗时 Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + ":Future<String> asyncTask(String s),耗时:" + (endTime - startTime)); return AsyncResult.forValue(s); }
存在事务
@Async("asyncTaskExecutor") @Transactional public void asyncTaskForTransaction(Boolean exFlag) { //新增一个用户 TbUser tbUser = new TbUser(); tbUser.setUsername("huanzi-qch"); tbUser.setPassword("123456"); tbUserRepository.save(tbUser); if(exFlag){ //模拟异常 throw new RuntimeException("模拟异常"); } }
测试执行
有返回值
/** * 启动成功 */ @Bean public ApplicationRunner applicationRunner() { return applicationArguments -> { long startTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + ":开始调用异步业务");//有返回值,但主线程不需要用到返回值 Future<String> future = testService.asyncTask("huanzi-qch"); long endTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + ":调用异步业务结束,耗时:" + (endTime - startTime)); }; }
无返回值
/** * 启动成功 */ @Bean public ApplicationRunner applicationRunner() { return applicationArguments -> { long startTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + ":开始调用异步业务"); //无返回值 testService.asyncTask(); long endTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + ":调用异步业务结束,耗时:" + (endTime - startTime)); }; }
存在事务
/** * 启动成功 */ @Bean public ApplicationRunner applicationRunner() { return applicationArguments -> { long startTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + ":开始调用异步业务");//事务测试,事务正常提交 testService.asyncTaskForTransaction(false); long endTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + ":调用异步业务结束,耗时:" + (endTime - startTime)); }; }
模拟异常,事务回滚
/** * 启动成功 */ @Bean public ApplicationRunner applicationRunner() { return applicationArguments -> { long startTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + ":开始调用异步业务"); //事务测试,模拟异常事务回滚 testService.asyncTaskForTransaction(true); long endTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + ":调用异步业务结束,耗时:" + (endTime - startTime)); }; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了