springboot:使用异步注解@Async的那些坑

一、引言

在java后端开发中经常会碰到处理多个任务的情况,比如一个方法中要调用多个请求,然后把多个请求的结果合并后统一返回,一般情况下调用其他的请求一般都是同步的,也就是每个请求都是阻塞的,那么这个处理时间必定是很长的,有没有一种方法可以让多个请求异步处理那,答案是有的。

springboot中提供了很便利的方式可以解决上面的问题,那就是异步注解@Async。正确的使用该注解可以使你的程序飞起,相反如果使用不当那么并不会取到理想的效果。

二、获取异步执行结果

1、环境介绍

下面是我的controller,SyncController.java

  1. package com.atssg.controller;
  2. import com.atssg.service.MySyncService;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. @Slf4j
  9. @RestController
  10. @RequestMapping("/sync")
  11. public class SyncController {
  12. @Autowired
  13. private MySyncService syncService;
  14. @GetMapping(value = "/test")
  15. public String test() {
  16. String str=null;
  17. try {
  18. log.info("start");
  19. str = syncService.asyncMethod();
  20. log.info("str:{}", str);
  21. return str;
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. }
  25. return str;
  26. }
  27. }

在controller中就是调用下层的方法并返回,再看service层的类MySyncService.java

  1. package com.atssg.service;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Service;
  4. import java.util.concurrent.ExecutionException;
  5. import java.util.concurrent.Future;
  6. @Service
  7. public class MySyncService {
  8. @Autowired
  9. private SyncService syncService;
  10. /**
  11. * 异步方法
  12. *
  13. * @return
  14. * @throws InterruptedException
  15. * @throws ExecutionException
  16. */
  17. public String asyncMethod() throws InterruptedException, ExecutionException {
  18. Future<String> result1 = syncService.method1("I");
  19. Future<String> result2 = syncService.method2("love");
  20. Future<String> result3 = syncService.method3("async");
  21. String str = result1.get();
  22. String str2 = result2.get();
  23. String str3 = result3.get();
  24. String result = str + str2 + str3;
  25. return result;
  26. }
  27. /**
  28. * 同步方法
  29. *
  30. * @return
  31. * @throws InterruptedException
  32. * @throws ExecutionException
  33. */
  34. public String syncMethod() throws InterruptedException, ExecutionException {
  35. /*同步写法*/
  36. String str = syncService.method1("I").get();
  37. String str2 = syncService.method2("love").get();
  38. String str3 = syncService.method3("async").get();
  39. return str + str2 + str3;
  40. }
  41. }

上面便是service类,仅仅是调用下次异步层的方法,并取得返回值。上面类中有两个方法,其写法也类似但结果却大不相同,后面详说。

下面是异步层的方法,SyncService.java

  1. package com.atssg.service;
  2. import org.springframework.scheduling.annotation.Async;
  3. import org.springframework.scheduling.annotation.AsyncResult;
  4. import org.springframework.stereotype.Service;
  5. import java.util.concurrent.Future;
  6. @Service
  7. @Async
  8. public class SyncService {
  9. //@Async
  10. public Future<String> method1(String str) throws InterruptedException {
  11. Thread.sleep(1000*10);
  12. return new AsyncResult<>( str);
  13. }
  14. //@Async
  15. public Future<String> method2(String str) throws InterruptedException {
  16. Thread.sleep(1000*5);
  17. return new AsyncResult<>(str);
  18. }
  19. // @Async
  20. public Future<String> method3(String str) throws InterruptedException {
  21. Thread.sleep(1000*15);
  22. return new AsyncResult<>(str);
  23. }
  24. }

该类使用@Async注解,表明该类中所有的方法都是异步执行的,其中@Async可修饰类也可以修饰方法。

这便是所有的环境。

2、错误的方式

在MySyncService中有两个方法,先看其中一个方法

  1. public String syncMethod() throws InterruptedException, ExecutionException {
  2. /*同步写法*/
  3. String str = syncService.method1("I").get();
  4. String str2 = syncService.method2("love").get();
  5. String str3 = syncService.method3("async").get();
  6. return str + str2 + str3;
  7. }

这种写法是调用异步方法后立即调用get()方法,即获取结果,下面看测试结果,在controllor中调用该方法,下面看执行结果

  1. 2021-08-21 11:06:28.612 INFO 3584 --- [nio-8080-exec-1] com.atssg.controller.SyncController : start
  2. 2021-08-21 11:06:58.651 INFO 3584 --- [nio-8080-exec-1] com.atssg.controller.SyncController : str:Iloveasync

可以看到共执行了30s,在异步层的方法中的三个方法如下,

  1. //@Async
  2. public Future<String> method1(String str) throws InterruptedException {
  3. Thread.sleep(1000*10);
  4. return new AsyncResult<>( str);
  5. }
  6. //@Async
  7. public Future<String> method2(String str) throws InterruptedException {
  8. Thread.sleep(1000*5);
  9. return new AsyncResult<>(str);
  10. }
  11. // @Async
  12. public Future<String> method3(String str) throws InterruptedException {
  13. Thread.sleep(1000*15);
  14. return new AsyncResult<>(str);
  15. }

可以看到这三个方法分别是睡眠10s、5s、15s,这就很好理解了syncMethod()方法中的写法是同步的,未达到异步的目的,切记调用完异步方法进接着调用get()方法不是异步的方式,而是同步的。

3、正确方式

上面看了错误的用法,下面看正确的方式,

  1. public String asyncMethod() throws InterruptedException, ExecutionException {
  2. Future<String> result1 = syncService.method1("I");
  3. Future<String> result2 = syncService.method2("love");
  4. Future<String> result3 = syncService.method3("async");
  5. String str = result1.get();
  6. String str2 = result2.get();
  7. String str3 = result3.get();
  8. String result = str + str2 + str3;
  9. return result;
  10. }

这种方式是首先调用异步方法,然后分别调用get()方法,取得执行结果。下面看测试结果

  1. 2021-08-21 11:17:23.516 INFO 3248 --- [nio-8080-exec-1] com.atssg.controller.SyncController : start
  2. 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注解的定义如下,

  1. @Target({ElementType.TYPE, ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Async {
  5. String value() default "";
  6. }

可以看到该注解可以用在类及方法上,用在类上表示类中的所有方法都是异步的,用在方法上表示该方法是异步的。

四、总结

今天的文章分享到这里,主要分享了关于@Async注解在获取执行结果的时候的坑,一定要先调用异步方法,然后再调用get()方法,获取结果,其中get方法还有一个重载的,可以设置超时时间,即超过设置的超时时间便返回,不再等待,各位小伙伴可以自己试验。

  1. V get(long timeout, TimeUnit unit)
  2. throws InterruptedException, ExecutionException, TimeoutException;
  3. }

下次继续分享有关@Async注解使用的一些小细节,欢迎持续关注。

推荐阅读

[springboot:异步调用@Async]:

springboot:使用异步注解@Async的那些坑的更多相关文章

  1. springboot:嵌套使用异步注解@Async还会异步执行吗

    一.引言 在前边的文章<[springboot:使用异步注解@Async的那些坑>中介绍了使用@Async注解获取任务执行结果的错误用法,今天来分享下另外一种常见的错误. 二.代码演示 下 ...

  2. springboot:使用异步注解@Async的前世今生

    在前边的文章中,和小伙伴一起认识了异步执行的好处,以及如何进行异步开发,对,就是使用@Async注解,在使用异步注解@Async的过程中也存在一些坑,不过通过正确的打开方式也可以很好的避免,今天想和大 ...

  3. Spring中异步注解@Async的使用、原理及使用时可能导致的问题

    前言 其实最近都在研究事务相关的内容,之所以写这么一篇文章是因为前面写了一篇关于循环依赖的文章: <面试必杀技,讲一讲Spring中的循环依赖> 然后,很多同学碰到了下面这个问题,添加了S ...

  4. springboot之异步调用@Async

    原文:http://www.cnblogs.com/xuwenjin/p/8858050.html 引言: 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交 ...

  5. springboot:异步调用@Async

    在后端开发中经常遇到一些耗时或者第三方系统调用的情况,我们知道Java程序一般的执行流程是顺序执行(不考虑多线程并发的情况),但是顺序执行的效率肯定是无法达到我们的预期的,这时就期望可以并行执行,常规 ...

  6. 关于Dubbo和Spring异步注解@Async的冲突

    项目中难免会有异步处理的需求,像异步记录日志啦,异步发送邮件啦,而Dubbo又是现在主流的分布式框架,所有异步+Dubbo的组合是再所难免的 但博主是实践中发现Dubbo的服务并不能很好的跟Sprin ...

  7. Java中异步注解@Async的陷阱

    或许,你在Java后端添加异步过程时会这样处理,然后摇摇大摆.灰溜溜地闪,而实际的运行结果却并不是我们期望的那样.那么,现在就将试验结果记录如下,以便少走弯路. (一)在Controller层的公开接 ...

  8. SpringBoot使用异步线程池实现生产环境批量数据推送

    前言 SpringBoot使用异步线程池: 1.编写线程池配置类,自定义一个线程池: 2.定义一个异步服务: 3.使用@Async注解指向定义的线程池: 这里以我工作中使用过的一个案例来做描述,我所在 ...

  9. @Async异步注解与SpringBoot结合使用

    当你在service层需要启动异步线程去执行某些分支任务,又不希望显式使用Thread等线程相关类,只想专注于实现业务逻辑代码开发,可以使用@Async异步注解. 1. 使用@Async 异步注解 C ...

随机推荐

  1. 【源码篇】Flutter GetX深度剖析 | 我们终将走出自己的路(万字图文)

    前言 人心中的成见是一座大山,任你怎么努力都休想搬动. 这是电影<哪吒>里申公豹说的一句话,也是贯彻整部电影的一个主题:或许这句话引起了太多人的共鸣:35岁职场危机,大厂卡本科学历,无房无 ...

  2. docker安装应用整理

    nginx安装: docker run \ --name nginx \ --volume /var/data/nginx/nginx.conf:/etc/nginx/nginx.conf \ --v ...

  3. Python单元测试框架unittest之断言(assert)

    unittest中断言主要有三种类型: 1.基本的布尔断言,即:要么正确,要么错误的验证 2.比较断言,如比较两个变量的值(跟上面的布尔断言区别不大,主要是通过比较两个变量的值得出布尔值) 3.复杂断 ...

  4. Java多线程(下)

    线程同步 当多个线程访问一个对象时,有可能会发生污读,即读取到未及时更新的数据,这个时候就需要线程同步. 线程同步: 即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线 ...

  5. RegOpenKeyEx

    对注册表的操作是通过句柄来完成的,在对某个键下的键值进行操作的时候首先将该键进行打开,然后使用键句柄进行引用该键,操作完后要进行关闭: 注册键的根键不需要打开,他们的句柄是固定的,直接拿来用就是了. ...

  6. 一文说清OpenCL框架

    背景 Read the fucking official documents! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: 对不 ...

  7. include 与 Widget

    include :include 引入的页面动态加载数据时,需要在当前页面中写数据加载,而不是只在引入的页面中就可以了:

  8. Couchdb 任意命令执行漏洞(CVE-2017-12636)

    影响版本:小于 1.7.0 以及 小于 2.1.1 该漏洞是需要登录用户方可触发,如果不知道目标管理员密码,可以利用CVE-2017-12635先增加一个管理员用户 依次执行如下请求即可触发任意命令执 ...

  9. 2020国防科大综述:3D点云深度学习—综述(点云形状识别部分)

    目录 摘要 1.引言: 2.背景 2.1 数据集 2.2评价指标 3.3D形状分类 3.1基于多视图的方法 3.2基于体素的方法 3.3基于点的方法 3.3.1 点对多层感知机方法 3.3.2基于卷积 ...

  10. Vue单点登录控件代码分享

    这里提供一个Vue单点登录的demo给大家参考,希望对想了解的朋友有一些帮助. 具体的原理大家可以查看我的上篇文章 vue实现单点登录的N种方式 废话不多少直接上代码 这里分两套系统,一是登录系统的主 ...