CompletableFuture的正常,异常,timeout和cancel
背景
Java 8的CompletableFuture提供了強大的task管理能力,比如通知機制,以及更加簡單抽象的task管理。
本文中,將用CompletableFuture的 supplyAsync() 方法來創建一個異步task,task會完成特定的工作,并且返回一個String。
下面的 handleResult() 方法作為task完成時的回調函數(注意:task完成并不意味著task的實際工作一定已經運行結束了,比如timeout和cancel場景)。
public static void handleResult(String result) {System.out.println("==========result: " + result + "===========");}一般來說,task的完成,有以下幾種原因:
- 正常結束
- 異常結束
- timeout
- cancel
本文將以代碼示例,如何處理task的不同完成狀態。
注:本文中使用的有些方法(比如 completeOnTimeout() ),是Java 9提供的。
單獨流程
正常流程
使用 thenAccept() 方法來處理task結果,代碼如下:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello");future.thenAccept(e -> handleResult(e));try {Thread.sleep(10* 1000);} catch (InterruptedException e) {e.printStackTrace();}注:在主線程里面sleep一段時間,其目的是確保task在主線程結束前完成工作。
運行結果如下:
==========result: hello===========Process finished with exit code 0異常流程
使用 exceptionally() 方法來處理task的異常,代碼如下:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {throw new RuntimeException("xxx");});// future.thenAccept(e -> handleResult(e)); // will not take effectfuture.exceptionally(e -> {System.out.println("got exception: " + e.getClass() + ", " + e.getCause());handleResult("default exception result");return "default exception result";});try {Thread.sleep(10* 1000);} catch (InterruptedException e) {e.printStackTrace();}運行結果如下:
got exception: class java.util.concurrent.CompletionException, java.lang.RuntimeException: xxx ==========result: default exception result===========正常和異常流程合并處理
使用 handle() 方法,它有2個參數:task結果和task異常,代碼如下:
future.handle((r, e) -> {if (e != null) {System.out.println("got exception: " + e.getClass() + ", " + e.getCause());handleResult("default exception result");return "default exception result";} else {handleResult(r);return r;}});timeout流程
使用 completeOnTimeout() 方法,設置timeout時間,并且在timeout發生時指定task結果。代碼如下:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(5 * 1000);} catch (InterruptedException e) {e.printStackTrace();}return "hello";});future.completeOnTimeout("default timeout result", 3 * 1000, TimeUnit.MILLISECONDS);future.handle((r, e) -> {if (e != null) {System.out.println("got exception: " + e.getClass() + ", " + e.getCause());handleResult("default exception result");return "default exception result";} else {handleResult(r);return r;}});try {Thread.sleep(10* 1000);} catch (InterruptedException e) {e.printStackTrace();}運行結果如下:
==========result: default timeout result===========注意:在 handle() 方法里走的是正常流程,而不是異常流程。
注:也可以用 orTimeout() 方法,指定timeout時間,則在timeout發生時,task會拋出 TimeoutException 異常。
注意:timeout并不會真正停止task的運行,也不會給task發interrupt信號。
cancel流程
使用 cancel() 方法來cancel task,并拋出 CancellationException 異常。代碼如下:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(5 * 1000);} catch (InterruptedException e) {e.printStackTrace();}return "hello";});future.handle((r, e) -> {if (e != null) {System.out.println("got exception: " + e.getClass() + ", " + e.getCause());handleResult("default exception result");return "default exception result";} else {handleResult(r);return r;}});try {Thread.sleep(3 * 1000);} catch (InterruptedException e) {e.printStackTrace();}future.cancel(true);try {Thread.sleep(10* 1000);} catch (InterruptedException e) {e.printStackTrace();}運行結果如下:
got exception: class java.util.concurrent.CancellationException, null ==========result: default exception result===========注意:cancel操作的觸發時機不可知,一般是在類之外被觸發的,所以本例中把cancel操作放在了最后(前面都是對future的基本操作)。
注意:cancel時,在 handle() 方法里走的是異常流程,其Exception為 CancellationException 。
注意:timeout并不會真正停止task的運行,也不會給task發interrupt信號。
合并流程
現在,考慮所有4種情況,把邏輯處理合到一起,代碼如下:
package com.example.test0721;import org.slf4j.Logger; import org.slf4j.LoggerFactory;import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit;public class Test0724 {public static void handleResult(String result) {System.out.println("============result: " + result + "=============");}public static void main(String[] args) {Logger log = LoggerFactory.getLogger(Test0724.class);log.info("main: started");CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {log.info("async: started");try {Thread.sleep(10 * 1000);} catch (InterruptedException e) {log.info("async: interrupted");return "default interrupted result";}boolean exceptional = false;if (exceptional) {log.info("async: run into exception");throw new RuntimeException("async exception");} else {log.info("async: finished");return "hello";}}).completeOnTimeout("default timeout result", 100 * 1000, TimeUnit.MILLISECONDS);// attention: split the line, because the cancel operation is against the above "future"future.handle((result, throwable) -> {log.info("async: result: " + result + ", throwable: " + throwable);if (throwable != null) {log.info("async: got exception from async: " + throwable.getClass() + ", " + throwable.getCause());handleResult("default exception result");return "default exception result";} else {log.info("got normal result: " + result);handleResult(result);return result;}});// try { // Thread.sleep(2 * 1000); // } catch (InterruptedException e) { // e.printStackTrace(); // } // // log.info("default cancel value"); // future.cancel(true); // log.info("main: async cancelled");try {Thread.sleep(15 * 1000);} catch (InterruptedException e) {e.printStackTrace();}log.info("main ended");} }正常流程
直接運行代碼,就是正常流程,運行結果如下:
02:33:39.239 [main] INFO com.example.test0721.Test0724 - main: started 02:33:39.259 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: started 02:33:49.265 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: finished 02:33:49.298 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: result: hello, throwable: null 02:33:49.299 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - got normal result: hello ============result: hello============= 02:33:54.268 [main] INFO com.example.test0721.Test0724 - main ended異常流程
把 exceptional 變量設置為 true :
boolean exceptional = true;運行結果如下:
02:34:23.351 [main] INFO com.example.test0721.Test0724 - main: started 02:34:23.368 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: started 02:34:33.371 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: run into exception 02:34:33.410 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: result: null, throwable: java.util.concurrent.CompletionException: java.lang.RuntimeException: async exception 02:34:33.411 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: got exception from async: class java.util.concurrent.CompletionException, java.lang.RuntimeException: async exception ============result: default exception result============= 02:34:38.373 [main] INFO com.example.test0721.Test0724 - main endedtimeout流程
把task的timeout時間設置為5秒鐘:
Thread.sleep(5 * 1000);運行結果如下:
02:35:27.985 [main] INFO com.example.test0721.Test0724 - main: started 02:35:27.996 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: started 02:35:33.040 [CompletableFutureDelayScheduler] INFO com.example.test0721.Test0724 - async: result: default timeout result, throwable: null 02:35:33.042 [CompletableFutureDelayScheduler] INFO com.example.test0721.Test0724 - got normal result: default timeout result ============result: default timeout result============= 02:35:37.999 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: run into exception 02:35:43.007 [main] INFO com.example.test0721.Test0724 - main ended注意:timeout并不會真正停止task的運行,也不會給task發interrupt信號。本例中,由于把 exception 設置為 true ,可以看到console有 async: run into exception 的輸出。同理,假設task沒有拋異常,則最終將會在console顯示 async: finished 。
cancel流程
把下面的代碼反注釋:
try {Thread.sleep(2 * 1000);} catch (InterruptedException e) {e.printStackTrace();}log.info("default cancel value");future.cancel(true);log.info("main: async cancelled");handleResult("default cancel value");即:在task運行到2秒鐘的時候,cancel task。運行結果如下:
02:41:03.815 [main] INFO com.example.test0721.Test0724 - main: started 02:41:03.841 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: started 02:41:05.841 [main] INFO com.example.test0721.Test0724 - default cancel value 02:41:05.870 [main] INFO com.example.test0721.Test0724 - async: result: null, throwable: java.util.concurrent.CancellationException 02:41:05.871 [main] INFO com.example.test0721.Test0724 - async: got exception from async: class java.util.concurrent.CancellationException, null ============result: default exception result============= 02:41:05.875 [main] INFO com.example.test0721.Test0724 - main: async cancelled 02:41:13.842 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: run into exception 02:41:20.876 [main] INFO com.example.test0721.Test0724 - main ended注意:cancel并不會真正停止task的運行,也不會給task發interrupt信號。本例中,由于把 exception 設置為 true ,可以看到console有 async: run into exception 的輸出。同理,假設task沒有拋異常,則最終將會在console顯示 async: finished 。
注意:本例中沒有區分task自身的異常和cancel task造成的異常。若想取分的話,只需在 handle() 方法里對異常加以判斷。timeout異常也同理。
總結
以上是生活随笔為你收集整理的CompletableFuture的正常,异常,timeout和cancel的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vs2013 c++項目轉 vs2008
- 下一篇: 一步步搭建Retrofit+RxJava