• 2.1、无返回值的异步方法

  • 2.1、有返回值的异步方法

  • 3.1、方法级别重写Executor

  • 3.2、应用级别重写Executor

  • 3.3、自定义线程池配置

“异步调用”对应的是“同步调用”,

在实际开发中,有时候为了及时处理请求和进行响应,我们可能使用异步调用,同步调用指程序按照定义顺序依次执行,每一行程序都必须等待上一行程序执行完成之后才能执行;异步调用指程序在顺序执行时,不等待异步调用的语句返回结果就执行后面的程序。异步调用的实现有很多,例如多线程、定时任务、消息队列等。

这里学习使用@Async注解来实现异步调用。

1、@EnableAsync

首先,我们需要在启动类上添加  @EnableAsync 注解来声明开启异步方法。

@SpringBootApplication
@EnableAsync
public class SpringbootAsyncApplication {public static void main(String[] args) {SpringApplication.run(SpringbootAsyncApplication.class, args);}}

2、@Async

需要注意的,@Async在使用上有一些限制:

  • 它只能应用于public修饰的方法

  • 自调用–从同一个类中调用async方法,将不起作用

原因很简单:

  • 只有公共方法,才可以被代理。

  • 自调用不起作用,因为它越过了代理直接调用了方法。

2.1、无返回值的异步方法

这是一个异步运行的无返回值方法:

    @Asyncpublic void asyncMethodWithVoidReturnType() {System.out.println("异步无返回值方法 "+ Thread.currentThread().getName());}

实例:

  • AsyncTask:异步式任务类,定义了三个异步式方法。

/*** @Author 三分恶* @Date 2020/7/15* @Description 异步式任务*/
@Component
public class AsyncTask {Logger log= LoggerFactory.getLogger(AsyncTask.class);private Random random = new Random();/*** 定义三个异步式方法* @throws InterruptedException*/@Asyncpublic void taskOne() throws InterruptedException {long start = System.currentTimeMillis();//随机休眠若干毫秒Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("任务一执行完成耗时{}秒", (end - start)/1000f);}@Asyncpublic void taskTwo() throws InterruptedException {long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("任务二执行完成耗时{}秒", (end - start)/1000f);}@Asyncpublic void taskThree() throws InterruptedException {long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("任务三执行完成耗时{}秒", (end - start)/1000f);}}
  • 在测试类中调用三个异步式方法:

/*** @Author 三分恶* @Date 2020/7/15* @Description*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class AsyncTaskTest {@Autowiredprivate AsyncTask asyncTask;Logger log= LoggerFactory.getLogger(AsyncTaskTest.class);@Testpublic void doAsyncTasks(){try {long start = System.currentTimeMillis();//调用三个异步式方法asyncTask.taskOne();asyncTask.taskTwo();asyncTask.taskThree();Thread.sleep(5000);long end = System.currentTimeMillis();log.info("主程序执行完成耗时{}秒", (end - start)/1000f);} catch (InterruptedException e) {e.printStackTrace();}}}

运行结果:可以看到三个方法没有顺序执行,这个复执行单元测试,您可能会遇到各种不同的结果,比如:

  • 没有任何任务相关的输出

    • 乱序的任务相关的输出

    • 有部分任务相关的输出

在这里插入图片描述

在这里插入图片描述

原因是目前doTaskOne、doTaskTwo、doTaskThree三个函数的时候已经是异步执行了。主程序在异步调用之后,主程序并不会理会这三个函数是否执行完成了,由于没有其他需要执行的内容,所以程序就自动结束了,导致了不完整或是没有输出任务相关内容的情况。

2.1、有返回值的异步方法

@Async也可以应用有返回值的方法–通过在Future中包装实际的返回值:

   /*** 有返回值的异步方法* @return*/@Asyncpublic Future<String> asyncMethodWithReturnType() {System.out.println("执行有返回值的异步方法 "+ Thread.currentThread().getName());try {Thread.sleep(5000);return new AsyncResult<String>("hello world !!!!");} catch (InterruptedException e) {//}return null;}

Spring还提供了一个实现Future的AsyncResult类。这个类可用于跟踪异步方法执行的结果。

实例:

  • 我们将2.1的实例改造成有返回值的异步方法:

    @Asyncpublic Future<String> taskOne() throws InterruptedException {long start = System.currentTimeMillis();//随机休眠若干毫秒Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("任务一执行完成耗时{}秒", (end - start)/1000f);return new AsyncResult<>("任务一完事了");}

taskTwo、taskThree方法做同样的改造。

  • 测试有返回值的异步方法:

   @Testpublic void doFutureTask(){try {long start=System.currentTimeMillis();Future<String> future1=asyncTask.taskOne();Future <String> future2 = asyncTask.taskTwo();Future <String> future3 = asyncTask.taskThree();//三个任务执行完再执行主程序do {Thread.sleep(100);} while (future1.isDone() && future2.isDone() && future3.isDone());log.info("获取异步方法的返回值:{}", future1.get());Thread.sleep(5000);long end = System.currentTimeMillis();log.info("主程序执行完成耗时{}秒", (end - start)/1000f);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}

运行结果:可以看到三个任务完成后才执行主程序,还输出了异步方法的返回值。

在这里插入图片描述

3、 Executor

默认情况下,Spring使用SimpleAsyncTaskExecutor异步运行这些方法。

可以在两个级别上重写默认线程池——应用程序级别或方法级别。

3.1、方法级别重写Executor

所需的执行程序需要在配置类中声明 Executor:

/*** @Author 三分恶* @Date 2020/7/15* @Description 方法级别重写线程池*/
@Configuration
@EnableAsync
public class SpringAsyncConfig {@Bean(name = "threadPoolTaskExecutor")public Executor threadPoolTaskExecutor() {return new ThreadPoolTaskExecutor();}
}

然后,在@Async中的属性提供Executor名称:

    @Async("threadPoolTaskExecutor")public void asyncMethodWithConfiguredExecutor() {System.out.println("Execute method with configured executor - "+ Thread.currentThread().getName());}

3.2、应用级别重写Executor

配置类应实现AsyncConfigurer接口,重写getAsyncExecutor()方法。

在这里,我们将返回整个应用程序的Executor,这样一来,它就成为运行以@Async注释的方法的默认Executor:

/*** @Author 三分恶* @Date 2020/7/15* @Description 应用级别重写 Excutor*/
@Configuration
@EnableAsync
public class SpringApplicationAsyncConfig implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {return new ThreadPoolTaskExecutor();}
}

3.3、自定义线程池配置

在上面,自定义线程池只是简单地返回了一个线程池:

return new ThreadPoolTaskExecutor();

实际上,还可以对线程池做一些配置:

/*** @Author 三分恶* @Date 2020/7/15* @Description*/
@Configuration
@EnableAsync
public class SpringPropertiesAsyncConfig implements AsyncConfigurer {/*** 对线程池进行配置* @return*/@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();taskExecutor.setCorePoolSize(20);taskExecutor.setMaxPoolSize(200);taskExecutor.setQueueCapacity(25);taskExecutor.setKeepAliveSeconds(200);taskExecutor.setThreadNamePrefix("oKong-");// 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());taskExecutor.initialize();return taskExecutor;}
}

ThreadPoolTaskExecutor配置参数的简单说明:

  • corePoolSize:线程池维护线程的最少数量

  • keepAliveSeconds:允许的空闲时间,当超过了核心线程出之外的线程在空闲时间到达之后会被销毁

  • maxPoolSize:线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程

  • queueCapacity:缓存队列

  • rejectedExecutionHandler:线程池对拒绝任务(无线程可用)的处理策略。这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务。还有一个是AbortPolicy策略:处理程序遭到拒绝将抛出运行时RejectedExecutionException。

4、异常处理

当方法返回类型为Future时,异常处理很容易– Future.get()方法将抛出异常。

但是如果是无返回值的异步方法,异常不会传播到调用线程。因此,我们需要添加额外的配置来处理异常。

我们将通过实现AsyncUncaughtExceptionHandler接口来创建自定义异步异常处理程序。

当存在任何未捕获的异步异常时,将调用handleUncaughtException()方法:

/*** @Author 三分恶* @Date 2020/7/15* @Description*/
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {@Overridepublic void handleUncaughtException(Throwable throwable, Method method, Object... objects) {System.out.println("Exception message - " + throwable.getMessage());System.out.println("Method name - " + method.getName());for (Object param : objects) {System.out.println("Parameter value - " + param);}}
}

上面,我们使用配置类实现了AsyncConfigurer接口。

作为其中的一部分,我们还需要重写getAsyncUncaughtExceptionHandler()方法以返回我们的自定义异步异常处理:

    /*** 返回自定义异常处理* @return*/@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new CustomAsyncExceptionHandler();}

5、总结

这里异步请求的使用及相关配置,如超时,异常等处理。在剥离一些和业务无关的操作时,就可以考虑使用异步调用进行其他无关业务操作,以此提供业务的处理效率。或者一些业务场景下可拆分出多个方法进行同步执行又互不影响时,也可以考虑使用异步调用方式提供执行效率。


SpringBoot异步调用相关推荐

  1. SpringBoot异步调用方法

    SpringBoot异步调用方法 一.spring boot--使用异步请求,提高系统的吞吐量 https://blog.csdn.net/liuchuanhong1/article/details/ ...

  2. springboot 多线程_SpringBoot异步调用@Async

    一. 什么是异步调用? 异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到上一步执行完后才能执行,异步调用则无需等待上一步程序执行完即可执行. 二. 如何实现异步调用 ...

  3. springboot之异步调用@Async

    引言: 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在spring 3. ...

  4. 面试官 | SpringBoot 中如何实现异步请求和异步调用?

    作者 | 会炼钢的小白龙 来源 | cnblogs.com/baixianlong/p/10661591.html 一.SpringBoot中异步请求的使用 1.异步请求与同步请求 特点: 可以先释放 ...

  5. SpringBoot利用@Async注解实现异步调用

    前言:异步编程是让程序并发运行的一种手段,使用异步编程可以大大提高我们程序的吞吐量,减少用户的等待时间.在Java并发编程中实现异步功能,一般是需要使用线程或者线程池.而实现一个线程,要么继承Thre ...

  6. springboot定时发送短信_spring boot 1.5.4 定时任务和异步调用(十)

    1Spring Boot定时任务和异步调用 我们在编写Spring Boot应用中经常会遇到这样的场景,比如:我需要定时地发送一些短信.邮件之类的操作,也可能会定时地检查和监控一些标志.参数等. sp ...

  7. springboot 异步mysql_spring boot 使用@Async实现异步调用方法

    使用@Async实现异步调用 什么是"异步调用"与"同步调用" "同步调用"就是程序按照一定的顺序依次执行,,每一行程序代码必须等上一行代码 ...

  8. springboot服务调用超时_Spring Boot 异步请求和异步调用,一文搞定

    一.Spring Boot中异步请求的使用 1.异步请求与同步请求 特点: 可以先释放容器分配给请求的线程与相关资源,减轻系统负担,释放了容器所分配线程的请求,其响应将被延后,可以在耗时处理完成(例如 ...

  9. Spring Boot 中使用@Async实现异步调用,加速任务执行!

    欢迎关注方志朋的博客,回复"666"获面试宝典 什么是"异步调用"?"异步调用"对应的是"同步调用",同步调用指程序按照 ...

最新文章

  1. python中time模块获取时间的使用
  2. 韩顺平老师 Mysql优化 笔记
  3. DigSci科学数据挖掘大赛-亚军方案分享
  4. 1083. List Grades (25)
  5. Oracle零碎要点---多表联合查询,收集数据库基本资料
  6. 有谁了解 最基础的计算机的网络结构呢?
  7. Ajax 学习(一)
  8. Laravel User Agent 轻松识别客户端(微信)信息(2019版)
  9. 回溯法之迷宫问题(华为笔试题)
  10. 解惑:Redis的HSCAN命令中COUNT参数的失效场景
  11. 协同软件解决方案集合
  12. 计算机运行瓶颈,我的电脑瓶颈在哪呢?
  13. php发出声音,电容也会发出声音!电容啸叫是怎么产生的?如何解决?
  14. 基尔霍夫定律及其应用
  15. C语言数据结构——环形队列
  16. H3C服务器修改启动项,H3C服务器 iFIST快速安装指南-6W102
  17. 自动化测试实战笔记--网易云实现QQ登录
  18. XBox 无法登录 错误码0x80070520 解决方案
  19. 【Unity3D】关于 InputManager 以及改键功能的制作
  20. 阿里百秀后台管理项目笔记 ---- Day01

热门文章

  1. 亿图AI助手一键生成思维导图,捕捉万千灵感,快速出彩!
  2. GitHub之深入解析如何创建、维护和管理自己的项目
  3. 文通尹总和华南区小伙伴的合照
  4. ARM 看门狗定时器
  5. 计算机毕业设计ssm双峰县在线房屋租售网站
  6. MOSSProject 2007语言包安装和部署
  7. 宝岛眼镜ERP上线的经验和教训
  8. 基于EEMD方法的降噪与解调在轴承故障诊断中的应用实证研究
  9. 旧电脑千万不要扔掉!它的剩余价值多得超乎想象!
  10. php将数据传到七牛云上,线上数据怎么热迁移到七牛云