1. 程式人生 > >SpringBoot整合篇(二) 非同步呼叫Async

SpringBoot整合篇(二) 非同步呼叫Async

什麼是非同步呼叫?

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

如何實現非同步呼叫?

多執行緒,這是很多人第一眼想到的關鍵詞,沒錯,多執行緒就是一種實現非同步呼叫的方式。

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

瀏覽器輸出:

task任務總耗時:4015ms

非同步呼叫成功,並且在所有任務都完成時程式才返回了結果!