springboot:使用异步注解@Async的那些坑
springboot:使用异步注解@Async的那些坑
一、引言
在java后端开发中经常会碰到处理多个任务的情况,比如一个方法中要调用多个请求,然后把多个请求的结果合并后统一返回,一般情况下调用其他的请求一般都是同步的,也就是每个请求都是阻塞的,那么这个处理时间必定是很长的,有没有一种方法可以让多个请求异步处理那,答案是有的。
springboot中提供了很便利的方式可以解决上面的问题,那就是异步注解@Async。正确的使用该注解可以使你的程序飞起,相反如果使用不当那么并不会取到理想的效果。
二、获取异步执行结果
1、环境介绍
下面是我的controller,SyncController.java
package com.atssg.controller;
import com.atssg.service.MySyncService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/sync")
public class SyncController {
@Autowired
private MySyncService syncService;
@GetMapping(value = "/test")
public String test() {
String str=null;
try {
log.info("start");
str = syncService.asyncMethod();
log.info("str:{}", str);
return str;
} catch (Exception e) {
e.printStackTrace();
}
return str;
}
}
在controller中就是调用下层的方法并返回,再看service层的类MySyncService.java
package com.atssg.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@Service
public class MySyncService {
@Autowired
private SyncService syncService;
/**
* 异步方法
*
* @return
* @throws InterruptedException
* @throws ExecutionException
*/
public String asyncMethod() throws InterruptedException, ExecutionException {
Future<String> result1 = syncService.method1("I");
Future<String> result2 = syncService.method2("love");
Future<String> result3 = syncService.method3("async");
String str = result1.get();
String str2 = result2.get();
String str3 = result3.get();
String result = str + str2 + str3;
return result;
}
/**
* 同步方法
*
* @return
* @throws InterruptedException
* @throws ExecutionException
*/
public String syncMethod() throws InterruptedException, ExecutionException {
/*同步写法*/
String str = syncService.method1("I").get();
String str2 = syncService.method2("love").get();
String str3 = syncService.method3("async").get();
return str + str2 + str3;
}
}
上面便是service类,仅仅是调用下次异步层的方法,并取得返回值。上面类中有两个方法,其写法也类似但结果却大不相同,后面详说。
下面是异步层的方法,SyncService.java
package com.atssg.service;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import java.util.concurrent.Future;
@Service
@Async
public class SyncService {
//@Async
public Future<String> method1(String str) throws InterruptedException {
Thread.sleep(1000*10);
return new AsyncResult<>( str);
}
//@Async
public Future<String> method2(String str) throws InterruptedException {
Thread.sleep(1000*5);
return new AsyncResult<>(str);
}
// @Async
public Future<String> method3(String str) throws InterruptedException {
Thread.sleep(1000*15);
return new AsyncResult<>(str);
}
}
该类使用@Async注解,表明该类中所有的方法都是异步执行的,其中@Async可修饰类也可以修饰方法。
这便是所有的环境。
2、错误的方式
在MySyncService中有两个方法,先看其中一个方法
public String syncMethod() throws InterruptedException, ExecutionException {
/*同步写法*/
String str = syncService.method1("I").get();
String str2 = syncService.method2("love").get();
String str3 = syncService.method3("async").get();
return str + str2 + str3;
}
这种写法是调用异步方法后立即调用get()方法,即获取结果,下面看测试结果,在controllor中调用该方法,下面看执行结果
2021-08-21 11:06:28.612 INFO 3584 --- [nio-8080-exec-1] com.atssg.controller.SyncController : start
2021-08-21 11:06:58.651 INFO 3584 --- [nio-8080-exec-1] com.atssg.controller.SyncController : str:Iloveasync
可以看到共执行了30s,在异步层的方法中的三个方法如下,
//@Async
public Future<String> method1(String str) throws InterruptedException {
Thread.sleep(1000*10);
return new AsyncResult<>( str);
}
//@Async
public Future<String> method2(String str) throws InterruptedException {
Thread.sleep(1000*5);
return new AsyncResult<>(str);
}
// @Async
public Future<String> method3(String str) throws InterruptedException {
Thread.sleep(1000*15);
return new AsyncResult<>(str);
}
可以看到这三个方法分别是睡眠10s、5s、15s,这就很好理解了syncMethod()方法中的写法是同步的,未达到异步的目的,切记调用完异步方法进接着调用get()方法不是异步的方式,而是同步的。
3、正确方式
上面看了错误的用法,下面看正确的方式,
public String asyncMethod() throws InterruptedException, ExecutionException {
Future<String> result1 = syncService.method1("I");
Future<String> result2 = syncService.method2("love");
Future<String> result3 = syncService.method3("async");
String str = result1.get();
String str2 = result2.get();
String str3 = result3.get();
String result = str + str2 + str3;
return result;
}
这种方式是首先调用异步方法,然后分别调用get()方法,取得执行结果。下面看测试结果
2021-08-21 11:17:23.516 INFO 3248 --- [nio-8080-exec-1] com.atssg.controller.SyncController : start
2021-08-21 11:17:38.535 INFO 3248 --- [nio-8080-exec-1] com.atssg.controller.SyncController : str:Iloveasync
执行时间未15s,这就很好解释了,异步层的三个方法,分别睡眠的时间是10s、5s、15s,既然是异步执行的,那么总的执行时间肯定是三个方法中最长的那个,符合测试结果。这才@Async正确的打开姿势。
三、异步执行@Async注解
@Async注解的定义如下,
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
String value() default "";
}
可以看到该注解可以用在类及方法上,用在类上表示类中的所有方法都是异步的,用在方法上表示该方法是异步的。
四、总结
今天的文章分享到这里,主要分享了关于@Async注解在获取执行结果的时候的坑,一定要先调用异步方法,然后再调用get()方法,获取结果,其中get方法还有一个重载的,可以设置超时时间,即超过设置的超时时间便返回,不再等待,各位小伙伴可以自己试验。
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
下次继续分享有关@Async注解使用的一些小细节,欢迎持续关注。
推荐阅读

springboot:使用异步注解@Async的那些坑的更多相关文章
- springboot:嵌套使用异步注解@Async还会异步执行吗
一.引言 在前边的文章<[springboot:使用异步注解@Async的那些坑>中介绍了使用@Async注解获取任务执行结果的错误用法,今天来分享下另外一种常见的错误. 二.代码演示 下 ...
- springboot:使用异步注解@Async的前世今生
在前边的文章中,和小伙伴一起认识了异步执行的好处,以及如何进行异步开发,对,就是使用@Async注解,在使用异步注解@Async的过程中也存在一些坑,不过通过正确的打开方式也可以很好的避免,今天想和大 ...
- Spring中异步注解@Async的使用、原理及使用时可能导致的问题
前言 其实最近都在研究事务相关的内容,之所以写这么一篇文章是因为前面写了一篇关于循环依赖的文章: <面试必杀技,讲一讲Spring中的循环依赖> 然后,很多同学碰到了下面这个问题,添加了S ...
- springboot之异步调用@Async
原文:http://www.cnblogs.com/xuwenjin/p/8858050.html 引言: 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交 ...
- springboot:异步调用@Async
在后端开发中经常遇到一些耗时或者第三方系统调用的情况,我们知道Java程序一般的执行流程是顺序执行(不考虑多线程并发的情况),但是顺序执行的效率肯定是无法达到我们的预期的,这时就期望可以并行执行,常规 ...
- 关于Dubbo和Spring异步注解@Async的冲突
项目中难免会有异步处理的需求,像异步记录日志啦,异步发送邮件啦,而Dubbo又是现在主流的分布式框架,所有异步+Dubbo的组合是再所难免的 但博主是实践中发现Dubbo的服务并不能很好的跟Sprin ...
- Java中异步注解@Async的陷阱
或许,你在Java后端添加异步过程时会这样处理,然后摇摇大摆.灰溜溜地闪,而实际的运行结果却并不是我们期望的那样.那么,现在就将试验结果记录如下,以便少走弯路. (一)在Controller层的公开接 ...
- SpringBoot使用异步线程池实现生产环境批量数据推送
前言 SpringBoot使用异步线程池: 1.编写线程池配置类,自定义一个线程池: 2.定义一个异步服务: 3.使用@Async注解指向定义的线程池: 这里以我工作中使用过的一个案例来做描述,我所在 ...
- @Async异步注解与SpringBoot结合使用
当你在service层需要启动异步线程去执行某些分支任务,又不希望显式使用Thread等线程相关类,只想专注于实现业务逻辑代码开发,可以使用@Async异步注解. 1. 使用@Async 异步注解 C ...
随机推荐
- Quartz:Quartz定时代码实现
1.添加pom.xml <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId> ...
- [小工具] chrome上日语翻译工具
rikaikun -> 日语 "理解君" 下载地址: https://chrome.google.com/webstore/detail/rikaikun/jipdnfibh ...
- WPF使用Microsoft.VisualBasic创建单例模式引起的权限降低问题
在进行WPF开发时,总是在找更加优雅去写单例模式的代码. 很多人都喜欢用Mutex,一个App.cs下很多的Mutex,我也喜欢用. 看完<WPF编程宝典>的第七章Applicaton类后 ...
- vmware使用U盘安装系统
创建好系统 创建一个新的硬盘,选择"physicalDrive1" 如果识别不到physicalDrive 1,使用下面的方法. 1.在本机的服务里面启用下面的服务. 2.重启 V ...
- File类与常用IO流第四章——IO字节流
一切文件在存储时,都是以二进制数字的形式保存的,都是一个一个字节.无论使用什么样的流对象,底层传输的始终是二进制数据. 字节输出流 OutputStream java.io.OutputStream ...
- springboot多个service互相调用的事务处理(十三)
在一个service的方法A中,调用另一个service的方法B,方法A和方法B均存在数据库插入操作,需要添加如下配置: @Transactional(rollbackFor = Exception. ...
- SSM中如何上传图片
1.文件配置 2.jsp页面 文件的name值不能跟数据库列名一致 3.控制层收集数据转发到逻辑层 4.逻辑层处理把用户信息存到数据库 5.注册成功后跳到jsp页面进行展示
- 本地图片转base64编码
通常获取图片的base64编码都是通过input的上传file属性获取转化,但是有时候需要的是本地图片不经过上传操作,直接拿本地图片转成base64编码就不行了,input上传操作需要人为操作一下,没 ...
- Webmin 远程命令执行漏洞(CVE-2019-15107)
影响版本 Webmin 1.920及以下版本 poc地址 https://github.com/Mr-xn/Penetration_Testing_POC/tree/master/CVE-2019-1 ...
- CS229 斯坦福大学机器学习复习材料(数学基础) - 线性代数
CS229 斯坦福大学机器学习复习材料(数学基础) - 线性代数 线性代数回顾与参考 1 基本概念和符号 1.1 基本符号 2 矩阵乘法 2.1 向量-向量乘法 2.2 矩阵-向量乘法 2.3 矩阵- ...