SpringBoot中@Async的用法与实践
介绍
在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务。其实,在Spring 3.x之后,就已经内置了@Async来完美解决这个问题,本文将完成介绍@Async的用法。
@Async的使用
注意事项
- 必须用在public方法上,且不能是static方法
- 不能与调用的方法在同一个类中
- 需要把该方法注入到Spring容器中,就是在一个类中添加异步方法,并在此类上使用@Component之类的注解加入到容器
注解配置开启
在springboot启动类上添加 @EnableAsync 注解
@EnableAsync
public class CupidApplication {
基于@Async无返回值调用
/**
* 没有返回值的Async方法
*/
@Override
@Async
public void asyncMethodWithVoidReturnType() {
log.info("没有返回值的Async方法, ThreadName : {}", Thread.currentThread().getName());
}
使用的方式非常简单,一个标注即可解决所有的问题。
调用结果:
2020-08-27 11:52:31.197 INFO 7311 --- [ task-5] c.w.cupid.service.impl.AsyncServiceImpl : 没有返回值的Async方法, ThreadName : task-5
基于@Async返回值的调用
/**
* 有返回值的Async方法
* @return Future new AsyncResult
*/
@Override
@Async
public Future<String> asyncMethodWithReturnType() {
log.info("有返回值的Async方法, ThreadName : {}", Thread.currentThread().getName());
try {
Thread.sleep(5000);
return new AsyncResult<String>("hello world !!!!");
} catch (InterruptedException e) {
log.error("出问题了, {}", e.getMessage());
}
return null;
}
以上示例可以发现,返回的数据类型为Future类型,其为一个接口。具体的结果类型为AsyncResult,这个是需要注意的地方。
调用返回结果的异步方法示例:
@GetMapping("/future")
public String futureAsync() throws ExecutionException, InterruptedException {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Invoking an asynchronous method. threadName : " + Thread.currentThread().getName());
Future<String> stringFuture = asyncService.asyncMethodWithReturnType();
while (true) {
if (stringFuture.isDone()) {
stringBuilder.append("Result from asynchronous process - " + stringFuture.get());
break;
}
stringBuilder.append("Continue doing something else.");
Thread.sleep(1000);
}
return stringBuilder.toString();
}
分析: 这些获取异步方法的结果信息,是通过不停的检查Future的状态来获取当前的异步方法是否执行完毕来实现的。
给@Async使用自定义的线程池
@Configuration
public class ThreadPoolConfig {
/**
* logger
*/
private final static Logger logger = LoggerFactory.getLogger(ThreadPoolConfig.class);
@Value("${asyncThreadPool.corePoolSize}")
private int corePoolSize;
@Value("${asyncThreadPool.maxPoolSize}")
private int maxPoolSize;
@Value("${asyncThreadPool.queueCapacity}")
private int queueCapacity;
@Value("${asyncThreadPool.keepAliveSeconds}")
private int keepAliveSeconds;
@Value("${asyncThreadPool.awaitTerminationSeconds}")
private int awaitTerminationSeconds;
@Value("${asyncThreadPool.threadNamePrefix}")
private String threadNamePrefix;
/**
* 线程池配置
* @param
* @return java.util.concurrent.Executor
* @author wliduo
* @date 2019/2/15 14:44
*/
@Bean(name = "threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
logger.info("---------- 线程池开始加载 ----------");
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
// 核心线程池大小
threadPoolTaskExecutor.setCorePoolSize(corePoolSize);
// 最大线程数
threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);
// 队列容量
threadPoolTaskExecutor.setQueueCapacity(keepAliveSeconds);
// 活跃时间
threadPoolTaskExecutor.setKeepAliveSeconds(queueCapacity);
// 主线程等待子线程执行时间
threadPoolTaskExecutor.setAwaitTerminationSeconds(awaitTerminationSeconds);
// 线程名字前缀
threadPoolTaskExecutor.setThreadNamePrefix(threadNamePrefix);
// RejectedExecutionHandler:当pool已经达到max-size的时候,如何处理新任务
// CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 初始化
threadPoolTaskExecutor.initialize();
logger.info("---------- 线程池加载完成 ----------");
return threadPoolTaskExecutor;
}
}
使用自定义的线程池
@Async(“threadPoolTaskExecutor”)
只需要在 @Async 注解的括号里加上自定义的线程池的bean即可
@Async调用中的事务处理机制
在@Async标注的方法,同时也适用了@Transactional进行了标注;在其调用数据库操作之时,将无法产生事务管理的控制,原因就在于其是基于异步处理的操作。
那该如何给这些操作添加事务管理呢?可以将需要事务管理操作的方法放置到异步方法内部,在内部被调用的方法上添加@Transactional。
例如:
方法A,使用了@Async/@Transactional来标注,但是无法产生事务控制的目的。
方法B,使用了@Async来标注,B中调用了C、D。C/D分别使用@Transactional做了标注,则可实现事务控制的目的。
参考资料
https://www.jianshu.com/p/0e61693860b7?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weixin-friends
https://blog.csdn.net/zhuyu19911016520/article/details/99992668
https://www.jianshu.com/p/49b9d15456d9?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weixin-friends
本文地址:https://blog.csdn.net/MCmango/article/details/113995203
下一篇: Java 通过mave命令下载jar