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 AsyncApplicationWithAnnotation { private static final Logger log = LoggerFactory.getLogger(AsyncApplicationWithAnnotation.class); /** * 自定義非同步執行緒池 * @return */ @Bean public AsyncTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setThreadNamePrefix("Anno-Executor"); executor.setMaxPoolSize(10); // 設定拒絕策略 executor.setRejectedExecutionHandler(new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { // ..... } }); // 使用預定義的異常處理類 // executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); return executor; } public static void main(String[] args) { log.info("Start AsyncApplication.. "); SpringApplication.run(AsyncApplicationWithAnnotation.class, args); } }
以上的異常方法和普通的方法呼叫相同
@RunWith(SpringRunner.class)
@SpringBootTest(classes=AsyncApplicationWithAnnotation.class)
public class AsyncApplicationWithAnnotationTests {
@Autowired
private AsyncDemo asyncDemo;
@Test
public void contextLoads() throws InterruptedException, ExecutionException {
asyncDemo.asyncInvokeSimplest();
asyncDemo.asyncInvokeWithParameter("test");
Future<String> future = asyncDemo.asyncInvokeReturnFuture(100);
System.out.println(future.get());
}
}
@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";
}
}
非同步任務類
控制檯:
-
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
非同步呼叫成功,並且在所有任務都完成時程式才返回了結果!
文章轉載自:https://blog.csdn.net/v2sking/article/details/72795742