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

1. Spring Boot使用@Async實現異步調用

鏈接:#link

原文: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

1.1. Spring Boot啓動類,增加@EnableAsync註解配置:

src/main/java/com/training/SpringBootServlet.java

[java] view plain copy

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);  
    }  
  
}  

1.2. 測試:

增加異步方法Service,線程休眠:

[java] view plain copy

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<>("任務三完成");  
    }  
      
}  

1.3. 調用異步測試測試,查看控制檯輸出執行順序:

[java] view plain copy

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

1.4. 什麼是異步調用?

異步調用是相對於同步調用而言的,同步調用是指程序按預定順序一步步執行,每一步必須等到上一步執行完後才能執行,異步調用則無需等待上一步程序執行完即可執行。

1.5. 如何實現異步調用?

多線程,這是很多人第一眼想到的關鍵詞,沒錯,多線程就是一種實現異步調用的方式。

在非spring目項目中我們要實現異步調用的就是使用多線程方式,可以自己實現Runable接口或者集成Thread類,或者使用jdk1.5以上提供了的Executors線程池。

1.6. StrngBoot中則提供了很方便的方式執行異步調用。

按照官方示例開擼

代碼入下

1.6.1. maven依賴:

[java] view plain copy

<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>  

1.6.2. 啓動類:添加@EnableAsync註解

[java] view plain copy

@SpringBootApplication  
@EnableAsync  
public class Application{  
  
    public static void main(String[] args) {  
        SpringApplication.run(Application.class, args);  
    }  
}  

1.6.3. Controller

只需在需要異步執行方法上添加@Async註解

[java] view plain copy

@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");  
    }  
}  

1.6.4. main函數運行spirngboot項目,啓動完成後瀏覽器訪問:

http://localhost:8080/

1.6.5. 控制檯:

[html] view plain copy
 
task1任務耗時:1012ms  
task2任務耗時:2009ms  
task3任務耗時:3004ms  
 
等了一段瀏覽器時候輸出入下:

 

[html] view plain copy
 
task任務總耗時:6002ms   

1.6.5.1. 異步並沒有執行!

難道是代碼寫錯了?反覆檢查了好幾遍,並沒有發現什麼明顯錯誤,想起spring對@Transactional註解時也有類似問題,spring掃描時具有@Transactional註解方法的類時,是生成一個代理類,由代理類去開啓關閉事務,而在同一個類中,方法調用是在類體內執行的,spring無法截獲這個方法調用。

1.6.5.2. 豁然開朗,將異步任務單獨放到一個類中,調整代碼入下:

1.6.6. Controller

[java] view plain copy

@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";  
          
    }  
}  

1.6.7. 異步任務類

[java] view plain copy

@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");  
    }  
}  

1.6.8. 控制檯:

[html] view plain copy

task1任務耗時:1012ms  
task2任務耗時:2009ms  
task3任務耗時:3004ms  

訪問瀏覽器結果入下:

[html] view plain copy

task任務總耗時:19ms  

1.6.9. 異步調用成功!

1.7. 如何知道三個異步任務什麼時候執行完,執行的結果怎樣呢?可以採用添加Fature回調方式判斷

代碼入下:

1.7.1. 異步任務類

[java] view plain copy

@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執行完畢");  
    }  
}  

1.7.2. Controller

[java] view plain copy

@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;  
    }  
}  

1.7.3. 控制檯輸出:

[html] view plain copy

task1任務耗時:1000ms  
task2任務耗時:2001ms  
task3任務耗時:3001ms  

1.7.4. 瀏覽器輸出:

[html] view plain copy

task任務總耗時:4015ms

1.7.5. 異步調用成功,並且在所有任務都完成時程序才返回了結果!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章