Spring Boot中使用@Async實現異步調用

什麼是「異步調用」?併發

「異步調用」對應的是「同步調用」,同步調用指程序按照定義順序依次執行,每一行程序都必須等待上一行程序執行完成以後才能執行;異步調用指程序在順序執行時,不等待異步調用的語句返回結果就執行後面的程序。dom

同步調用

下面經過一個簡單示例來直觀的理解什麼是同步調用:異步

  • 定義Task類,建立三個處理函數分別模擬三個執行任務的操做,操做消耗時間隨機取(10秒內)
 

1函數

2單元測試

3測試

4spa

5.net

6code

7對象

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

 

@Component

public class Task {

public static Random random =new Random();

public void doTaskOne() throws Exception {

System.out.println("開始作任務一");

long start = System.currentTimeMillis();

Thread.sleep(random.nextInt(10000));

long end = System.currentTimeMillis();

System.out.println("完成任務一,耗時:" + (end - start) + "毫秒");

}

public void doTaskTwo() throws Exception {

System.out.println("開始作任務二");

long start = System.currentTimeMillis();

Thread.sleep(random.nextInt(10000));

long end = System.currentTimeMillis();

System.out.println("完成任務二,耗時:" + (end - start) + "毫秒");

}

public void doTaskThree() throws Exception {

System.out.println("開始作任務三");

long start = System.currentTimeMillis();

Thread.sleep(random.nextInt(10000));

long end = System.currentTimeMillis();

System.out.println("完成任務三,耗時:" + (end - start) + "毫秒");

}

}

  • 在單元測試用例中,注入Task對象,並在測試用例中執行doTaskOnedoTaskTwodoTaskThree三個函數。
 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

 

@RunWith(SpringJUnit4ClassRunner.class)

@SpringApplicationConfiguration(classes = Application.class)

public class ApplicationTests {

@Autowired

private Task task;

@Test

public void test() throws Exception {

task.doTaskOne();

task.doTaskTwo();

task.doTaskThree();

}

}

  • 執行單元測試,能夠看到相似以下輸出:
 

1

2

3

4

5

6

 

開始作任務一

完成任務一,耗時:4256毫秒

開始作任務二

完成任務二,耗時:4957毫秒

開始作任務三

完成任務三,耗時:7173毫秒

任務1、任務2、任務三順序的執行完了,換言之doTaskOnedoTaskTwodoTaskThree三個函數順序的執行完成。

異步調用

上述的同步調用雖然順利的執行完了三個任務,可是能夠看到執行時間比較長,若這三個任務自己之間不存在依賴關係,能夠併發執行的話,同步調用在執行效率方面就比較差,能夠考慮經過異步調用的方式來併發執行。

在Spring Boot中,咱們只須要經過使用@Async註解就能簡單的將原來的同步函數變爲異步函數,Task類改在爲以下模式:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

 

@Component

public class Task {

@Async

public void doTaskOne() throws Exception {

// 同上內容,省略

}

@Async

public void doTaskTwo() throws Exception {

// 同上內容,省略

}

@Async

public void doTaskThree() throws Exception {

// 同上內容,省略

}

}

爲了讓@Async註解可以生效,還須要在Spring Boot的主程序中配置@EnableAsync,以下所示:

 

1

2

3

4

5

6

7

8

9

 

@SpringBootApplication

@EnableAsync

public class Application {

public static void main(String[] args) {

SpringApplication.run(Application.class, args);

}

}

此時能夠反覆執行單元測試,您可能會遇到各類不一樣的結果,好比:

  • 沒有任何任務相關的輸出
  • 有部分任務相關的輸出
  • 亂序的任務相關的輸出

緣由是目前doTaskOnedoTaskTwodoTaskThree三個函數的時候已是異步執行了。主程序在異步調用以後,主程序並不會理會這三個函數是否執行完成了,因爲沒有其餘須要執行的內容,因此程序就自動結束了,致使了不完整或是沒有輸出任務相關內容的狀況。

注: @Async所修飾的函數不要定義爲static類型,這樣異步調用不會生效

異步回調

爲了讓doTaskOnedoTaskTwodoTaskThree能正常結束,假設咱們須要統計一下三個任務併發執行共耗時多少,這就須要等到上述三個函數都完成調動以後記錄時間,並計算結果。

那麼咱們如何判斷上述三個異步調用是否已經執行完成呢?咱們須要使用Future<T>來返回異步調用的結果,就像以下方式改造doTaskOne函數:

 

1

2

3

4

5

6

7

8

9

 

@Async

public Future<String> doTaskOne() throws Exception {

System.out.println("開始作任務一");

long start = System.currentTimeMillis();

Thread.sleep(random.nextInt(10000));

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

 

@Test

public void test() throws Exception {

long start = System.currentTimeMillis();

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(1000);

}

long end = System.currentTimeMillis();

System.out.println("任務所有完成,總耗時:" + (end - start) + "毫秒");

}

看看咱們作了哪些改變:

  • 在測試用例一開始記錄開始時間
  • 在調用三個異步函數的時候,返回Future<String>類型的結果對象
  • 在調用完三個異步函數以後,開啓一個循環,根據返回的Future<String>對象來判斷三個異步函數是否都結束了。若都結束,就結束循環;若沒有都結束,就等1秒後再判斷。
  • 跳出循環以後,根據結束時間 - 開始時間,計算出三個任務併發執行的總耗時。

執行一下上述的單元測試,能夠看到以下結果:

 

1

2

3

4

5

6

7

 

開始作任務一

開始作任務二

開始作任務三

完成任務三,耗時:37毫秒

完成任務二,耗時:3661毫秒

完成任務一,耗時:7149毫秒

任務所有完成,總耗時:8025毫秒

能夠看到,經過異步調用,讓任務1、2、三併發執行,有效的減小了程序的總運行時間。

相關文章
相關標籤/搜索