当前位置: 首页 > 后端技术 > Java

在SpringBoot中使用@Async实现异步调用,加速任务执行!

时间:2023-04-02 09:42:57 Java

什么是“异步调用”?“异步调用”对应“同步调用”。同步调用是指程序按照定义好的顺序依次执行。每行程序必须等待上一行程序执行完成后才能执行;异步调用是指程序在顺序执行时不等待异步调用。如果语句返回结果,将执行下面的程序。同步调用下面通过一个简单的例子来直观的理解什么是同步调用:定义Task类,创建三个处理函数模拟执行任务的三个操作,操作时间随机选择(10秒以内)@Slf4j@Componentpublic类AsyncTasks{publicstaticRandomrandom=newRandom();publicvoiddoTaskOne()throwsException{log.info("开始任务一");长启动=System.currentTimeMillis();Thread.sleep(random.nextInt(10000));长端=System.currentTimeMillis();log.info("完成任务一的时间:"+(end-start)+"milliseconds");}publicvoiddoTaskTwo()throwsException{log.info("开始做任务二");长启动=System.currentTimeMillis();Thread.sleep(random.nextInt(10000));长端=System.currentTimeMillis();log.info("完成任务二,耗时:"+(end-start)+"毫秒");}publicvoiddoTaskThree()throwsException{log.info("开始做任务三");长启动=System.currentTimeMillis();Thread.sleep(random.nextInt(10000));长端=System.currentTimeMillis();log.info("完成任务三,耗时:"+(end-start)+"milliseconds");}}在单元测试用例中,注入Task对象,执行测试用例中的三个函数doTaskOne、doTaskTwo、doTaskThree@Slf4j@SpringBootTest公共类Chapter75ApplicationTests{@AutowiredprivateAsyncTasksasyncTasks;@Testpublicvoidtest()throwsException{asyncTasks.doTaskOne();asyncTasks.doTaskTwo();asyncTasks.doTaskThree();输出类似如下:2021-09-1123:19:12.922INFO92539---[main]com.didispace.chapter75.AsyncTasks:starttaskone2021-09-1123:19:17.788INFO92539---[main]com.didispace.chapter75.AsyncTasks:完成任务1,耗时:4865毫秒2021-09-1123:19:17.788INFO92539---[main]com.didispace.chapter75.AsyncTasks:开始任务22021-09-1123:19:24.851INFO92539---[main]com.didispace.chapter75.AsyncTasks:完成任务二,耗时:7063毫秒2021-09-1123:19:24.851INFO92539---[main]com.didispace.chapter75.AsyncTasks:Starttask32021-09-1123:19:26.928INFO92539---[main]com.didispace.chapter75.AsyncTasks:完成任务3,耗时:2076毫秒任务1.任务2,任务3序列也就是说,依次执行doTaskOne、doTaskTwo、doTaskThree这三个函数,完成异步调用。上面的同步调用虽然成功执行了三个任务,但是可以看出执行时间比较长。如果存在依赖关系,可以并发执行,同步调用的执行效率比较差,可以考虑异步调用并发执行。在SpringBoot中,我们只需要使用@Async注解,就可以简单的把原来的同步函数变成异步函数,Task类改为如下模式:@Slf4j@ComponentpublicclassAsyncTasks{publicstaticRandomrandom=newRandom();@AsyncpublicvoiddoTaskOne()throwsException{log.info("开始任务一");长启动=System.currentTimeMillis();Thread.sleep(random.nextInt(10000));长端=系统。当前时间毫秒();log.info("完成任务一的时间:"+(end-start)+"milliseconds");}@AsyncpublicvoiddoTaskTwo()throwsException{log.info("开始任务二");长启动=System.currentTimeMillis();Thread.sleep(random.nextInt(10000));长端=System.currentTimeMillis();log.info("完成任务二,耗时:"+(end-start)+"毫秒");}@AsyncpublicvoiddoTaskThree()throwsException{log.info("开始做任务三");长启动=System.currentTimeMillis();Thread.sleep(random.nextInt(10000));长端=System.currentTimeMillis();log.info("完成任务三,耗时:"+(end-start)+"milliseconds");}}为了让@Async注解生效,还需要在SpringBoot的主程序中配置@EnableAsync,如下:@EnableAsync@SpringBootApplicationpublicclassChapter75Application{publicstaticvoidmain(String[]args){SpringApplication.run(Chapter75Application.class,args);}}此时可以反复执行单元测试,可能会遇到各种结果,比如:notask-relatedoutputwithpartial任务相关输出乱序的原因是doTaskOne,doTaskTwo,doTaskThree这三个函数已经被异步执行过,异步调用后,主程序并不关心这三个函数是否执行functions完成后,由于没有其他内容需要执行,程序自动结束,导致任务相关内容不完整或者没有输出。注意:@Async修饰的函数不要定义为static类型,这样异步调用不会生效异步回调为了让doTaskOne、doTaskTwo、doTaskThree正常结束,假设我们需要统计这三个任务并发执行的总耗时。这就需要把上面三个函数全部调完后,记录时间,计算结果。那么我们如何判断以上三个异步调用是否已经执行呢?我们需要使用CompletableFuture来返回异步调用的结果,就像改造doTaskOne函数一样:@AsyncpublicCompletableFuturedoTaskOne()throwsException{log.info("开始做任务一");longstart=System.currentTimeMillis();Thread.sleep(random.nextInt(10000));长端=System.currentTimeMillis();log.info("任务1完成,耗时:"+(end-start)+"milliseconds");returnCompletableFuture.completedFuture("Taskcompleted");}像上面那样修改另外两个异步函数之后,我们来改造一下测试用例,让测试等待三个异步调用完成之后再做其他事情。@Testpublicvoidtest()throwsException{longstart=System.currentTimeMillis();CompletableFuturetask1=asyncTasks.doTaskOne();CompletableFuturetask2=asyncTasks.doTaskTwo();CompletableFuturetask3=asyncTasks。doTaskThree();CompletableFuture.allOf(task1,task2,task3).join();长端=System.currentTimeMillis();log.info("Alltaskscompleted,totaltime:"+(end-start)+"Milliseconds");}看看我们做了什么改动:在测试用例的开始,记录开始时间。当调用三个异步函数时,通过CompletableFuture.allOf(task1,task2,task3)返回类型为CompletableFuture的结果对象。join()实现三个异步任务完成前的阻塞效果。三个任务完成后,根据结束时间-开始时间计算三个任务并发执行的总耗时。执行上面的单元测试,可以看到如下结果:2021-09-1123:33:38.842INFO95891---[task-3]com.didispace.chapter75.AsyncTasks:Starttask32021-09-1123:33:38.842INFO95891---[task-2]com.didispace.chapter75.AsyncTasks:开始执行任务二2021-09-1123:33:38.842INFO95891---[task-1]com.didispace。chapter75.AsyncTasks:开始任务12021-09-1123:33:45.155INFO95891---[task-2]com.didispace.chapter75.AsyncTasks:完成任务2,耗时:6312毫秒2021-09-1123:33:47.308INFO95891---[task-3]com.didispace.chapter75.AsyncTasks:完成任务三,耗时:8465毫秒2021-09-1123:33:47.403INFO95891---[task-1]com.didispace.chapter75.AsyncTasks:任务1完成,耗时:8560毫秒2021-09-1123:33:47.404INFO95891---[main]c.d.chapter75.Chapter75ApplicationTests:所有任务完成,总时间-consuming:8590milliseconds可以看出,通过异步调用,任务1、2、3并发执行,有效减少了程序的总运行时间。本系列教程《Spring Boot 2.x基础教程》直接点击!,欢迎收藏转发!如果你在学习过程中遇到困难?大家可以加入我们的Spring技术交流群,参与交流讨论,更好的学习进步!代码示例本文的完整工程可以在下面仓库2.x目录下的chapter7-5工程中查看:Github:https://github.com/dyc87112/SpringBoot-Learning/Gitee:https://gitee.com/didispace/SpringBoot-Learning/如果您觉得本文不错,欢迎Star支持,您的关注是我坚持的动力!欢迎关注我的公众号:程序员DD,分享别处看不到的知识和思考