一、引言
在java后端開發中經常會碰到處理多個任務的情況,比如一個方法中要調用多個請求,然后把多個請求的結果合并后統一返回,一般情況下調用其他的請求一般都是同步的,也就是每個請求都是阻塞的,那么這個處理時間必定是很長的,有沒有一種方法可以讓多個請求異步處理那,答案是有的。
springboot中提供了很便利的方式可以解決上面的問題,那就是異步注解@Async。正確的使用該注解可以使你的程序飛起,相反如果使用不當那么并不會取到理想的效果。
二、獲取異步執行結果
1、環境介紹
下面是我的controller,SyncController.java
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
|
package com.atssg.controller; import com.atssg.service.MySyncService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController @RequestMapping ( "/sync" ) public class SyncController { @Autowired private MySyncService syncService; @GetMapping (value = "/test" ) public String test() { String str= null ; try { log.info( "start" ); str = syncService.asyncMethod(); log.info( "str:{}" , str); return str; } catch (Exception e) { e.printStackTrace(); } return str; } } |
在controller中就是調用下層的方法并返回,再看service層的類MySyncService.java
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
package com.atssg.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @Service public class MySyncService { @Autowired private SyncService syncService; /** * 異步方法 * * @return * @throws InterruptedException * @throws ExecutionException */ public String asyncMethod() throws InterruptedException, ExecutionException { Future<String> result1 = syncService.method1( "I" ); Future<String> result2 = syncService.method2( "love" ); Future<String> result3 = syncService.method3( "async" ); String str = result1.get(); String str2 = result2.get(); String str3 = result3.get(); String result = str + str2 + str3; return result; } /** * 同步方法 * * @return * @throws InterruptedException * @throws ExecutionException */ public String syncMethod() throws InterruptedException, ExecutionException { /*同步寫法*/ String str = syncService.method1( "I" ).get(); String str2 = syncService.method2( "love" ).get(); String str3 = syncService.method3( "async" ).get(); return str + str2 + str3; } } |
上面便是service類,僅僅是調用下次異步層的方法,并取得返回值。上面類中有兩個方法,其寫法也類似但結果卻大不相同,后面詳說。
下面是異步層的方法,SyncService.java
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
|
package com.atssg.service; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Service; import java.util.concurrent.Future; @Service @Async public class SyncService { //@Async public Future<String> method1(String str) throws InterruptedException { Thread.sleep( 1000 * 10 ); return new AsyncResult<>( str); } //@Async public Future<String> method2(String str) throws InterruptedException { Thread.sleep( 1000 * 5 ); return new AsyncResult<>(str); } // @Async public Future<String> method3(String str) throws InterruptedException { Thread.sleep( 1000 * 15 ); return new AsyncResult<>(str); } } |
該類使用@Async注解,表明該類中所有的方法都是異步執行的,其中@Async可修飾類也可以修飾方法。
這便是所有的環境。
2、錯誤的方式
在MySyncService中有兩個方法,先看其中一個方法
1
2
3
4
5
6
7
|
public String syncMethod() throws InterruptedException, ExecutionException { /*同步寫法*/ String str = syncService.method1( "I" ).get(); String str2 = syncService.method2( "love" ).get(); String str3 = syncService.method3( "async" ).get(); return str + str2 + str3; } |
這種寫法是調用異步方法后立即調用get()方法,即獲取結果,下面看測試結果,在controllor中調用該方法,下面看執行結果
2021-08-21 11:06:28.612 INFO 3584 --- [nio-8080-exec-1] com.atssg.controller.SyncController : start
2021-08-21 11:06:58.651 INFO 3584 --- [nio-8080-exec-1] com.atssg.controller.SyncController : str:Iloveasync
可以看到共執行了30s,在異步層的方法中的三個方法如下,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//@Async public Future<String> method1(String str) throws InterruptedException { Thread.sleep( 1000 * 10 ); return new AsyncResult<>( str); } //@Async public Future<String> method2(String str) throws InterruptedException { Thread.sleep( 1000 * 5 ); return new AsyncResult<>(str); } // @Async public Future<String> method3(String str) throws InterruptedException { Thread.sleep( 1000 * 15 ); return new AsyncResult<>(str); } |
可以看到這三個方法分別是睡眠10s、5s、15s,這就很好理解了syncMethod()方法中的寫法是同步的,未達到異步的目的,切記調用完異步方法進接著調用get()方法不是異步的方式,而是同步的。
3、正確方式
上面看了錯誤的用法,下面看正確的方式,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public String asyncMethod() throws InterruptedException, ExecutionException { Future<String> result1 = syncService.method1( "I" ); Future<String> result2 = syncService.method2( "love" ); Future<String> result3 = syncService.method3( "async" ); String str = result1.get(); String str2 = result2.get(); String str3 = result3.get(); String result = str + str2 + str3; return result; } |
這種方式是首先調用異步方法,然后分別調用get()方法,取得執行結果。下面看測試結果
2021-08-21 11:17:23.516 INFO 3248 --- [nio-8080-exec-1] com.atssg.controller.SyncController : start
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
2
3
4
5
6
|
@Target ({ElementType.TYPE, ElementType.METHOD}) @Retention (RetentionPolicy.RUNTIME) @Documented public @interface Async { String value() default "" ; } |
可以看到該注解可以用在類及方法上,用在類上表示類中的所有方法都是異步的,用在方法上表示該方法是異步的。
四、總結
今天的文章分享到這里,主要分享了關于@Async注解在獲取執行結果的時候的坑,一定要先調用異步方法,然后再調用get()方法,獲取結果,其中get方法還有一個重載的,可以設置超時時間,即超過設置的超時時間便返回,不再等待,各位小伙伴可以自己試驗。
1
2
3
|
V get( long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; } |
下次繼續分享有關@Async注解使用的一些小細節,歡迎持續關注。
到此這篇關于詳解springboot使用異步注解@Async獲取執行結果的坑的文章就介紹到這了,更多相關springboot使用異步注解@Async 內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://www.cnblogs.com/teach/p/15169153.html