SpringMvc使用@Async注解实现有返回值和无返回值的异步处理

您所在的位置:网站首页 java接口异步返回结果是什么 SpringMvc使用@Async注解实现有返回值和无返回值的异步处理

SpringMvc使用@Async注解实现有返回值和无返回值的异步处理

2023-04-01 19:11| 来源: 网络整理| 查看: 265

1. SpringMvc使用@Async注解实现有返回值和无返回值的异步处理

异步调用对应的是同步调用,同步调用指程序按照定义顺序依次执行,每一行程序都必须等待上一行程序执行完成之后才能执行;异步调用指程序在顺序执行时,不等待异步调用的语句返回结果就执行后面的程序。

1.1 同步方式调用代码

相关代码

12345678910111213141516171819202122232425262728@Servicepublic class TaskService { public void doTaskOne() throws Exception { System.out.println("开始做任务一"); long start = System.currentTimeMillis(); Thread.sleep(2000); long end = System.currentTimeMillis(); System.out.println("完成任务二,耗时:%s" , (end - start) + "毫秒"); } public void doTaskTwo() throws Exception { System.out.println("开始做任务二"); long start = System.currentTimeMillis(); Thread.sleep(3000); long end = System.currentTimeMillis(); System.out.println("完成任务二,耗时:%s" , (end - start) + "毫秒"); } public void doTaskThree() throws Exception { System.out.println("开始做任务三"); long start = System.currentTimeMillis(); Thread.sleep(4000); long end = System.currentTimeMillis(); System.out.println("完成任务二,耗时:%s" , (end - start) + "毫秒"); }}

同步调用

1234567891011@Autowiredprivate TaskService task;public String test() { try { task.doTaskOne(); task.doTaskTwo(); task.doTaskThree(); }catch (Exception e){ }}

下面是运行结果,可以看到三个方法是依次执行的,分别耗时2秒、3秒、4秒、总耗时9秒

123456开始做任务一完成任务一,耗时:2001毫秒开始做任务二完成任务二,耗时:3000毫秒开始做任务三完成任务三,耗时:4001毫秒

上面的同步调用,虽然顺利地完成了三个任务,但是执行时间比较长,如果这三个任务没有依赖关系,可以并发执行的话,可以考虑使用异步调用的方法。

2. 异步方式调用代码无返回值 首先在spring中配置相关参数开启异步调用 123456789101112

如果直接按照下面的方式配置,则 Spring 会使用默认的线程池 org.springframework.core.task.SimpleAsyncTaskExecutor但这个 SimpleAsyncTaskExecutor 不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。

123456789101112131415# 详见spring-context-4.1.7.RELEASE.jar/org/springframework/scheduling/config/spring-task-4.0.xsd 的描述 Specifies the java.util.Executor instance to use when invoking asynchronous methods.If not provided, an instance of org.springframework.core.task.SimpleAsyncTaskExecutorwill be used by default.Note that as of Spring 3.1.2, individual @Async methods may qualify which executor touse, meaning that the executor specified here acts as a default for all non-qualified@Async methods. ]]> 在方法上加上 @Async 注解就能将同步函数变成异步函数,改造后的代码 123456789101112131415161718192021222324252627282930import java.util.concurrent.Future;@Servicepublic class TaskService { @Async public void doTaskOne() throws Exception { System.out.println("开始做任务一"); long start = System.currentTimeMillis(); Thread.sleep(2000); long end = System.currentTimeMillis(); System.out.println("完成任务二,耗时:%s" , (end - start) + "毫秒"); } @Async public void doTaskTwo() throws Exception { System.out.println("开始做任务二"); long start = System.currentTimeMillis(); Thread.sleep(3000); long end = System.currentTimeMillis(); System.out.println("完成任务二,耗时:%s" , (end - start) + "毫秒"); } @Async public void doTaskThree() throws Exception { System.out.println("开始做任务三"); long start = System.currentTimeMillis(); Thread.sleep(4000); long end = System.currentTimeMillis(); System.out.println("完成任务二,耗时:%s" , (end - start) + "毫秒"); }} 重新调用之后的运行结果如下: 123456开始做任务三开始做任务二开始做任务一完成任务一,耗时:2000毫秒完成任务二,耗时:3001毫秒完成任务三,耗时:4000毫秒

注意事项

@Async 所修饰的函数不要定义为 static 类型,这样异步调用不会生效。

调用方法和异步函数不能在一个 class 中。

可以在使用的时候自定义线程池 @Async("poolTaskExecutor")

3. 异步方式调用代码有返回值

如果想知道异步函数什么时候执行完,那就需要使用 Future (AsyncResult是Future的子类)来返回异步调用的结果。改造后的代码如下:

12345678910111213141516171819202122232425262728293031323334@Servicepublic class TaskService { @Async public Future doTaskOne() { System.out.println("开始做任务一"); long start = System.currentTimeMillis(); Thread.sleep(2000); long end = System.currentTimeMillis(); System.out.println("完成任务二,耗时:%s" , (end - start) + "毫秒"); return new AsyncResult("任务一完成"); } @Async public Future doTaskTwo() { System.out.println("开始做任务二"); long start = System.currentTimeMillis(); Thread.sleep(3000); long end = System.currentTimeMillis(); System.out.println("完成任务二,耗时:%s" , (end - start) + "毫秒"); return new AsyncResult("任务二完成"); } @Async public Future doTaskThree() { System.out.println("开始做任务三"); long start = System.currentTimeMillis(); Thread.sleep(4000); long end = System.currentTimeMillis(); System.out.println("完成任务二,耗时:%s" , (end - start) + "毫秒"); return new AsyncResult("任务三完成"); }} 异步方式调用代码有返回值 123456789101112131415161718192021222324252627282930@Autowiredprivate TaskService task;public List test() { List result = new ArrayList(16); StopWatch stopWatch = new StopWatch(); stopWatch.start("接口速度统计"); // 3秒超时 int timeout = 3; try { Future task1 = task.doTaskOne(); Future task2 = task.doTaskTwo(); Future task3 = task.doTaskThree(); String taskString1= task1.get(timeout, TimeUnit.SECONDS); String taskString2= task2.get(timeout, TimeUnit.SECONDS); String taskString3= task3.get(timeout, TimeUnit.SECONDS); result.add(taskString1); result.add(taskString2); result.add(taskString3); }catch (TimeoutException | InterruptedException | ExecutionException e){ return result; } finally { stopWatch.stop(); log.info(stopWatch.prettyPrint()); } return result;} 12345678910111213开始做任务三开始做任务二开始做任务一完成任务一,耗时:2001毫秒完成任务二,耗时:3000毫秒完成任务三,耗时:4001毫秒StopWatch '': running time (millis) = 248-----------------------------------------ms % Task name-----------------------------------------04036 100% 接口速度统计

刚开始想利用CountDownLatch来实现等待所有线程结束整合结果,后来调整为 Future 的 get(long timeout, TimeUnit unit) 来实现线程的超时控制,我看有些的例子使用死循环来阻塞整合线程的执行结果,这样做是有些问题的,如果有个线程一直没有结束运行,那就完犊子了!

这是一种常见的场景将一个大的任务切分为数个子任务,并行处理所有子任务,当所有子任务都成功结束时再继续处理后面的逻辑。还有一种做法是利用CountDownLatch, 主线程构造countDownLatch对象,latch的大小为子任务的总数,每一个任务持有countDownLatch的引用,任务完成时对latch减1,主线程阻塞在countDownLatch.await方法上,当所有子任务都成功执行完后,latch=0, 主线程继续执行。

总结

异步调用可以提升接口性能。比如导出下载、发送邮件短信等代码,可以使用异步执行。

参考 https://blog.csdn.net/qqfo24/article/details/81383022



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3