原文:http://blog.csdn.net/a286352250/article/details/53157822
項目GitHub地址 :
https://github.com/FrameReserve/TrainingBoot
Spring Boot(十)使用@Async實現異步調用 ,標記地址:
https://github.com/FrameReserve/TrainingBoot/releases/tag/0.0.10
Spring Boot啟動類,增加@EnableAsync注解配置:
src/main/java/com/training/SpringBootServlet.java
- package com.training;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.boot.builder.SpringApplicationBuilder;
- import org.springframework.boot.web.support.SpringBootServletInitializer;
- import org.springframework.scheduling.annotation.EnableAsync;
- @SpringBootApplication
- @EnableAsync
- public class SpringBootServlet extends SpringBootServletInitializer {
- // jar啟動
- public static void main(String[] args) {
- SpringApplication.run(SpringBootServlet.class, args);
- }
- // tomcat war啟動
- @Override
- protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
- return application.sources(SpringBootServlet.class);
- }
- }
測試:
增加異步方法Service,線程休眠:
- package com.training.async.service.impl;
- import java.util.Random;
- import java.util.concurrent.Future;
- import org.springframework.scheduling.annotation.Async;
- import org.springframework.scheduling.annotation.AsyncResult;
- import org.springframework.stereotype.Service;
- import com.training.async.service.DemoAsyncService;
- @Service
- public class DemoAsyncServiceImpl implements DemoAsyncService {
- public static Random random =new Random();
- @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<>("任務一完成");
- }
- @Async
- public Future<String> doTaskTwo() 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<>("任務二完成");
- }
- @Async
- public Future<String> doTaskThree() 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<>("任務三完成");
- }
- }
調用異步測試測試,查看控制台輸出執行順序:
- package com.training.async.controller;
- import io.swagger.annotations.ApiOperation;
- import java.util.concurrent.Future;
- import javax.annotation.Resource;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestMethod;
- import org.springframework.web.bind.annotation.ResponseBody;
- import org.springframework.web.bind.annotation.RestController;
- import com.training.async.service.DemoAsyncService;
- import com.training.core.dto.ResultDataDto;
- @RestController
- @RequestMapping(value="/async")
- public class DemoAsyncController {
- @Resource
- private DemoAsyncService demoAsyncService;
- /**
- * 測試異步方法調用順序
- */
- @ApiOperation(value="測試異步方法調用順序", notes="getEntityById")
- @RequestMapping(value = "/getTestDemoAsync", method = RequestMethod.GET)
- public @ResponseBody ResultDataDto getEntityById() throws Exception {
- long start = System.currentTimeMillis();
- Future<String> task1 = demoAsyncService.doTaskOne();
- Future<String> task2 = demoAsyncService.doTaskTwo();
- Future<String> task3 = demoAsyncService.doTaskThree();
- while(true) {
- if(task1.isDone() && task2.isDone() && task3.isDone()) {
- // 三個任務都調用完成,退出循環等待
- break;
- }
- Thread.sleep(1000);
- }
- long end = System.currentTimeMillis();
- System.out.println("任務全部完成,總耗時:" + (end - start) + "毫秒");
- return ResultDataDto.addSuccess();
- }
- }
原文:http://blog.csdn.net/v2sking/article/details/72795742
什么是異步調用?
異步調用是相對於同步調用而言的,同步調用是指程序按預定順序一步步執行,每一步必須等到上一步執行完后才能執行,異步調用則無需等待上一步程序執行完即可執行。
如何實現異步調用?
多線程,這是很多人第一眼想到的關鍵詞,沒錯,多線程就是一種實現異步調用的方式。
在非spring目項目中我們要實現異步調用的就是使用多線程方式,可以自己實現Runable接口或者集成Thread類,或者使用jdk1.5以上提供了的Executors線程池。
StrngBoot中則提供了很方便的方式執行異步調用。
按照官方示例開擼
代碼入下
maven依賴:
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>1.5.3.RELEASE</version>
- </parent>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- </dependencies>
啟動類:添加@EnableAsync注解
- @SpringBootApplication
- @EnableAsync
- public class Application{
- public static void main(String[] args) {
- SpringApplication.run(Application.class, args);
- }
- }
Controller
只需在需要異步執行方法上添加@Async注解
- @RestController
- @RequestMapping("")
- public class AsyncTaskController {
- @RequestMapping("")
- public String doTask() throws InterruptedException{
- long currentTimeMillis = System.currentTimeMillis();
- this.task1();
- this.task2();
- this.task3();
- long currentTimeMillis1 = System.currentTimeMillis();
- return "task任務總耗時:"+(currentTimeMillis1-currentTimeMillis)+"ms";
- }
- @Async
- public void task1() throws InterruptedException{
- long currentTimeMillis = System.currentTimeMillis();
- Thread.sleep(1000);
- long currentTimeMillis1 = System.currentTimeMillis();
- System.out.println("task1任務耗時:"+(currentTimeMillis1-currentTimeMillis)+"ms");
- }
- @Async
- public void task2() throws InterruptedException{
- long currentTimeMillis = System.currentTimeMillis();
- Thread.sleep(2000);
- long currentTimeMillis1 = System.currentTimeMillis();
- System.out.println("task2任務耗時:"+(currentTimeMillis1-currentTimeMillis)+"ms");
- }
- @Async
- public void task3() throws InterruptedException{
- long currentTimeMillis = System.currentTimeMillis();
- Thread.sleep(3000);
- long currentTimeMillis1 = System.currentTimeMillis();
- System.out.println("task3任務耗時:"+(currentTimeMillis1-currentTimeMillis)+"ms");
- }
- }
main函數運行spirngboot項目,啟動完成后瀏覽器訪問:
http://localhost:8080/
控制台:
- task1任務耗時:1012ms
- task2任務耗時:2009ms
- task3任務耗時:3004ms
等了一段瀏覽器時候輸出入下:
- task任務總耗時:6002ms
異步並沒有執行!
難道是代碼寫錯了?反復檢查了好幾遍,並沒有發現什么明顯錯誤,想起spring對@Transactional注解時也有類似問題,spring掃描時具有@Transactional注解方法的類時,是生成一個代理類,由代理類去開啟關閉事務,而在同一個類中,方法調用是在類體內執行的,spring無法截獲這個方法調用。
豁然開朗,將異步任務單獨放到一個類中,調整代碼入下:
Controller
- @RequestMapping("")
- @RestController
- public class AsyncTaskController {
- @Autowired
- private AsyncTask asyncTask;
- @RequestMapping("")
- public String doTask() throws InterruptedException{
- long currentTimeMillis = System.currentTimeMillis();
- asyncTask.task1();
- asyncTask.task2();
- asyncTask.task3();
- long currentTimeMillis1 = System.currentTimeMillis();
- return "task任務總耗時:"+(currentTimeMillis1-currentTimeMillis)+"ms";
- }
- }
異步任務類
- @Component
- public class AsyncTask {
- @Async
- public void task1() throws InterruptedException{
- long currentTimeMillis = System.currentTimeMillis();
- Thread.sleep(1000);
- long currentTimeMillis1 = System.currentTimeMillis();
- System.out.println("task1任務耗時:"+(currentTimeMillis1-currentTimeMillis)+"ms");
- }
- @Async
- public void task2() throws InterruptedException{
- long currentTimeMillis = System.currentTimeMillis();
- Thread.sleep(2000);
- long currentTimeMillis1 = System.currentTimeMillis();
- System.out.println("task2任務耗時:"+(currentTimeMillis1-currentTimeMillis)+"ms");
- }
- @Async
- public void task3() throws InterruptedException{
- long currentTimeMillis = System.currentTimeMillis();
- Thread.sleep(3000);
- long currentTimeMillis1 = System.currentTimeMillis();
- System.out.println("task3任務耗時:"+(currentTimeMillis1-currentTimeMillis)+"ms");
- }
- }
控制台:
- task1任務耗時:1012ms
- task2任務耗時:2009ms
- task3任務耗時:3004ms
訪問瀏覽器結果入下:
- task任務總耗時:19ms
異步調用成功!
如何知道三個異步任務什么時候執行完,執行的結果怎樣呢?可以采用添加Fature回調方式判斷
代碼入下:
異步任務類
- @Component
- public class AsyncTask {
- @Async
- public Future<String> task1() throws InterruptedException{
- long currentTimeMillis = System.currentTimeMillis();
- Thread.sleep(1000);
- long currentTimeMillis1 = System.currentTimeMillis();
- System.out.println("task1任務耗時:"+(currentTimeMillis1-currentTimeMillis)+"ms");
- return new AsyncResult<String>("task1執行完畢");
- }
- @Async
- public Future<String> task2() throws InterruptedException{
- long currentTimeMillis = System.currentTimeMillis();
- Thread.sleep(2000);
- long currentTimeMillis1 = System.currentTimeMillis();
- System.out.println("task2任務耗時:"+(currentTimeMillis1-currentTimeMillis)+"ms");
- return new AsyncResult<String>("task2執行完畢");
- }
- @Async
- public Future<String> task3() throws InterruptedException{
- long currentTimeMillis = System.currentTimeMillis();
- Thread.sleep(3000);
- long currentTimeMillis1 = System.currentTimeMillis();
- System.out.println("task3任務耗時:"+(currentTimeMillis1-currentTimeMillis)+"ms");
- return new AsyncResult<String>("task3執行完畢");
- }
- }
Controller
- @RequestMapping("")
- @RestController
- public class AsyncTaskController {
- @Autowired
- private AsyncTask asyncTask;
- @RequestMapping("")
- public String doTask() throws InterruptedException{
- long currentTimeMillis = System.currentTimeMillis();
- Future<String> task1 = asyncTask.task1();
- Future<String> task2 = asyncTask.task2();
- Future<String> task3 = asyncTask.task3();
- String result = null;
- for (;;) {
- if(task1.isDone() && task2.isDone() && task3.isDone()) {
- // 三個任務都調用完成,退出循環等待
- break;
- }
- Thread.sleep(1000);
- }
- long currentTimeMillis1 = System.currentTimeMillis();
- result = "task任務總耗時:"+(currentTimeMillis1-currentTimeMillis)+"ms";
- return result;
- }
- }
控制台輸出:
- task1任務耗時:1000ms
- task2任務耗時:2001ms
- task3任務耗時:3001ms
瀏覽器輸出:
- <span style="font-family: Simsun; font-size: 14px;">task任務總耗時:4015ms</span>
異步調用成功,並且在所有任務都完成時程序才返回了結果!