spring mvc 异步调用 @Async
https://blog.csdn.net/qqfo24/article/details/81383022
spring mvc 异步调用 @Async
yujunyi_ 2018-08-03 10:35:01 3044 收藏 4
分类专栏: java
版权
定义
“异步调用”对应的是“同步调用”,同步调用指程序按照定义顺序依次执行,每一行程序都必须等待上一行程序执行完成之后才能执行;异步调用指程序在顺序执行时,不等待异步调用的语句返回结果就执行后面的程序。
同步调用
通过一个例子,来看下同步调用。 写了一个 TaskService ,里面有三个方法,分别模拟耗时2秒、3秒、4秒的操作。
@Service
public class TaskService {
public void doTaskOne() throws Exception {
System.out.println("开始做任务一");
long start = System.currentTimeMillis();
Thread.sleep(2000);
long end = System.currentTimeMillis();
System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
}
public void doTaskTwo() throws Exception {
System.out.println("开始做任务二");
long start = System.currentTimeMillis();
Thread.sleep(3000);
long end = System.currentTimeMillis();
System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
}
public void doTaskThree() throws Exception {
System.out.println("开始做任务三");
long start = System.currentTimeMillis();
Thread.sleep(4000);
long end = System.currentTimeMillis();
System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
写一个 service ,调用这三个方法。
@Autowired
private TaskService task;
public String test() {
try {
task.doTaskOne();
task.doTaskTwo();
task.doTaskThree();
}catch (Exception e){
}
}
1
2
3
4
5
6
7
8
9
10
11
下面是运行结果,可以看到三个方法是依次执行的,分别耗时2秒、3秒、4秒、总耗时9秒。
14:38:13,846 INFO [stdout] (default task-8) 开始做任务一
14:38:15,848 INFO [stdout] (default task-8) 完成任务一,耗时:2001毫秒
14:38:15,850 INFO [stdout] (default task-8) 开始做任务二
14:38:18,850 INFO [stdout] (default task-8) 完成任务二,耗时:3000毫秒
14:38:18,852 INFO [stdout] (default task-8) 开始做任务三
14:38:22,853 INFO [stdout] (default task-8) 完成任务三,耗时:4001毫秒
1
2
3
4
5
6
异步调用
上面的同步调用,虽然顺利地完成了三个任务,但是执行时间比较长,如果这三个任务没有依赖关系,可以并发执行的话,可以考虑使用异步调用的方法。
在方法上加上 @Async 注解就能将同步函数变成异步函数,下面是更改后的代码:
@Service
public 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("完成任务一,耗时:" + (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("完成任务二,耗时:" + (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("完成任务三,耗时:" + (end - start) + "毫秒");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
另外需要在application.xml中开启异步调用。
<task:annotation-driven executor="targetExecutor" scheduler="targetScheduler"/>
<task:executor id="targetExecutor" pool-size="5"/>
<task:scheduler id="targetScheduler" pool-size="10"/>
1
2
3
4
调用方法不用改动,运行后结果如下:
14:49:16,001 INFO [stdout] (targetExecutor-1) 开始做任务三
14:49:16,259 INFO [stdout] (targetExecutor-5) 开始做任务二
14:49:16,260 INFO [stdout] (targetExecutor-4) 开始做任务一
14:49:18,261 INFO [stdout] (targetExecutor-4) 完成任务一,耗时:2000毫秒
14:49:19,260 INFO [stdout] (targetExecutor-5) 完成任务二,耗时:3001毫秒
14:49:20,002 INFO [stdout] (targetExecutor-1) 完成任务三,耗时:4000毫秒
1
2
3
4
5
6
可以看到3个任务异步执行,总耗时4秒。
注意
@Async 所修饰的函数不要定义为 static 类型,这样异步调用不会生效。
调用方法和异步函数不能在一个 class 中。
异步回调
如果想知道异步函数什么时候执行完,那就需要使用 Future 来返回异步调用的结果。
改造后的代码如下:
@Service
public class TaskService {
@Async
public Future<String> doTaskOne() throws Exception {
System.out.println("开始做任务一");
long start = System.currentTimeMillis();
Thread.sleep(2000);
long end = System.currentTimeMillis();
System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("任务一完成");
}
@Async
public Future<String> doTaskTwo() throws Exception {
System.out.println("开始做任务二");
long start = System.currentTimeMillis();
Thread.sleep(3000);
long end = System.currentTimeMillis();
System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("任务二完成");
}
@Async
public Future<String> doTaskThree() throws Exception {
System.out.println("开始做任务三");
long start = System.currentTimeMillis();
Thread.sleep(4000);
long end = System.currentTimeMillis();
System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("任务三完成");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
调用代码如下,增加了一个计算总耗时的:
long start = System.currentTimeMillis();
try {
Future<String> task1 = task.doTaskOne();
Future<String> task2 = task.doTaskTwo();
Future<String> task3 = task.doTaskThree();
while(true) {
if(task1.isDone() && task2.isDone() && task3.isDone()) {
// 三个任务都调用完成,退出循环等待
break;
}
Thread.sleep(100);
}
long end = System.currentTimeMillis();
System.out.println("任务全部完成,总耗时:" + (end - start) + "毫秒");
}catch (Exception e){
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
运行结果如下:
15:21:05,676 INFO [stdout] (targetExecutor-4) 开始做任务三
15:21:05,676 INFO [stdout] (targetExecutor-3) 开始做任务二
15:21:05,677 INFO [stdout] (targetExecutor-2) 开始做任务一
15:21:07,678 INFO [stdout] (targetExecutor-2) 完成任务一,耗时:2001毫秒
15:21:08,677 INFO [stdout] (targetExecutor-3) 完成任务二,耗时:3000毫秒
15:21:09,677 INFO [stdout] (targetExecutor-4) 完成任务三,耗时:4001毫秒
15:21:09,704 INFO [stdout] (default task-21) 任务全部完成,总耗时:4036毫秒
1
2
3
4
5
6
7
总结
异步调用可以让一些和主要逻辑无关的代码异步执行,以提升性能。比如一些日志的代码、发送邮件短信等代码,可以使用异步执行。
————————————————
版权声明:本文为CSDN博主「yujunyi_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qqfo24/java/article/details/81383022