CompletableFuture 異步處理任務總結

oracle JDK8 有關內容的文檔:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html

創建異步任務

runAsync 執行 CompletableFuture 任務,沒有返回值

static CompletableFuture<Void> runAsync(Runnable runnable)
static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)

supplyAsync 執行 CompletableFuture 任務,可有返回值

static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

如果不指定 Executor 實現,則使用 ForkJoinPool.commonPool() 作為執行異步代碼的線程池

創建異步任務后,可根據需求進行如下的操作:

方法名稱 類型 傳參 返回值
thenRun 單任務消費 無傳參 無返回值
thenRunAsync 單任務消費 無傳參 無返回值
thenApply 單任務消費 要傳參 有返回值
thenApplyAsync 單任務消費 要傳參 有返回值
thenAccept 單任務消費 要傳參 無返回值
thenAcceptAsync 單任務消費 要傳參 無返回值
thenCombine 雙任務消費(與) 要傳參(兩個任務的執行結果) 有返回值
thenCombineAsync 雙任務消費(與) 要傳參(兩個任務的執行結果) 有返回值
thenAcceptBoth 雙任務消費(與) 要傳參(兩個任務的執行結果) 無返回值
thenAcceptBothAsync 雙任務消費(與) 要傳參(兩個任務的執行結果) 無返回值
runAfterBoth 雙任務消費(與) 無傳參 無返回值
runAfterBothAsync 雙任務消費(與) 無傳參 無返回值
applyToEither 雙任務消費(或) 要傳參(已完成任務的執行結果) 有返回值
applyToEitherAsync 雙任務消費(或) 要傳參(已完成任務的執行結果) 有返回值
acceptEither 雙任務消費(或) 要傳參(已完成任務的執行結果) 無返回值
acceptEitherAsync 雙任務消費(或) 要傳參(已完成任務的執行結果) 無返回值
runAfterEither 雙任務消費(或) 無傳參 無返回值
runAfterEitherAsync 雙任務消費(或) 無傳參 無返回值
whenComplete 單任務消費 要傳參(正常返回值和異常) 無返回值
whenCompleteAsync 單任務消費 要傳參(正常返回值和異常) 無返回值
handle 單任務消費 要傳參(正常返回值和異常) 有返回值
handleAsync 單任務消費 要傳參(正常返回值和異常) 有返回值
exceptionally 單任務消費 要傳參 (異常) 無返回值
thenCompose 單任務消費 要傳參 有返回值
allOf 多任務消費(與) 要傳參(任務列表) 無返回值
anyOf 多任務消費(或) 要傳參(任務列表) 無返回值

不帶 Async 版本由上一個任務的線程繼續執行該任務,Async 版本可以指定執行該異步任務的 Executor 實現,如果不指定,默認使用 ForkJoinPool.commonPool()

單任務消費

回調方法 類型 傳參 返回值
thenRun 單任務消費 無傳參 無返回值
thenRunAsync 單任務消費 無傳參 無返回值
thenAccept 單任務消費 要傳參 無返回值
thenAcceptAsync 單任務消費 要傳參 無返回值
thenApply 單任務消費 要傳參 有返回值
thenApplyAsync 單任務消費 要傳參 有返回值
public static void main(String[] args) throws Exception {
    var executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    var supplyAsyncTask = CompletableFuture.supplyAsync(() -> {
        System.out.println("supplyAsyncTask=" + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "";
    }, executor);

    // thenApply
    var thenApplyTask = supplyAsyncTask.thenApply((param) -> {
        System.out.println("thenApplyTask=" + Thread.currentThread().getName());
        return "";
    });

    // thenApplyAsync不指定線程池
    var thenApplyAsyncTask = supplyAsyncTask.thenApplyAsync((param) -> {
        System.out.println("thenApplyAsyncTask=" + Thread.currentThread().getName());
        return "";
    });

    // thenApplyAsync指定線程池
    var thenApplyAsyncTask2 = supplyAsyncTask.thenApplyAsync((param) -> {
        System.out.println("thenApplyAsyncTask2=" + Thread.currentThread().getName());
        return "";
    }, executor);

    // 不調用get()將不執行回調
    thenApplyAsyncTask.get();
    thenApplyAsyncTask2.get();

    // 關閉線程池
    executor.shutdown();
}

輸出結果:

supplyAsyncTask=pool-1-thread-1
thenApplyAsyncTask2=pool-1-thread-2
thenApplyTask=pool-1-thread-2
thenApplyAsyncTask=ForkJoinPool.commonPool-worker-3

雙任務消費(與)

將兩個 CompletableFuture 組合起來,只有這兩個都正常執行完了,才會執行某個任務。

方法名稱 類型 傳參 返回值
thenCombine 雙任務消費(與) 有傳參(兩個任務的執行結果) 有返回值
thenCombineAsync 雙任務消費(與) 有傳參(兩個任務的執行結果) 有返回值
thenAcceptBoth 雙任務消費(與) 有傳參(兩個任務的執行結果) 無返回值
thenAcceptBothAsync 雙任務消費(與) 有傳參(兩個任務的執行結果) 無返回值
runAfterBoth 雙任務消費(與) 無傳參 無返回值
runAfterBothAsync 雙任務消費(與) 無傳參 無返回值
public static void main(String[] args) throws Exception {

    var task1 = CompletableFuture.supplyAsync(() -> "task1");
    var task2 = CompletableFuture.supplyAsync(() -> "task2");
    var task3 = CompletableFuture.supplyAsync(() -> "task3");

    task1.thenCombine(task2, (param1, param2) -> {
        // task1task2
        System.out.println(param1 + param2);
        return param1 + param2;
    }).thenCombine(task3, (param12, param3) -> {
        // task1task2task3
        System.out.println(param12 + param3);
        return param12 + param3;
    });

    task1.thenAcceptBoth(task2, (param1, param2) -> {
        // task1task2
        System.out.println(param1 + param2);
    }).thenAcceptBoth(task3, (param12, param3) -> {
        // nulltask3
        System.out.println(param12 + param3);
    });

    task1.runAfterBoth(task2, () -> {
        // task1 and task2
        System.out.println("task1 and task2");
    });
}

雙任務消費(或)

將兩個 CompletableFuture 組合起來,只要其中一個執行完了,就執行回調方法。

方法名稱 類型 傳參 返回值
applyToEither 雙任務消費(或) 有傳參(已完成任務的執行結果) 有返回值
applyToEitherAsync 雙任務消費(或) 有傳參(已完成任務的執行結果) 有返回值
acceptEither 雙任務消費(或) 有傳參(已完成任務的執行結果) 無返回值
acceptEitherAsync 雙任務消費(或) 有傳參(已完成任務的執行結果) 無返回值
runAfterEither 雙任務消費(或) 無傳參 無返回值
runAfterEitherAsync 雙任務消費(或) 無傳參 無返回值
public static void main(String[] args) throws Exception {

    var task1 = CompletableFuture.supplyAsync(() -> {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "task1";
    });
    var task2 = CompletableFuture.supplyAsync(() -> {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "task2";
    });
    var task3 = CompletableFuture.supplyAsync(() -> {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "task3";
    });

    task1.applyToEither(task2, (param) -> {
        // applyToEither=task2
        System.out.println("applyToEither=" + param);
        return param;
    }).acceptEither(task3, (param) -> {
        // acceptEither=task2 或 acceptEither=task3
        System.out.println("acceptEither=" + param);
    }).get();

    // task1 or task2
    task1.runAfterEither(task2,()-> System.out.println("task1 or task2"));
}

其他

whenComplete、whenCompleteAsync

某個任務執行完成后,執行的回調方法,無返回值。可以訪問 CompletableFuture 的結果和異常作為參數,使用它們并執行想要的操作。此方法并不能轉換完成的結果。會內部拋出異常。其正常返回的 CompletableFuture 的結果來自上個任務。

handle、handleAsync

不論正常返回還是出異常都會進入 handle,參數通常為 new BiFunction<T, Throwable, R>();,其中

  • T:上一任務傳入的對象類型
  • Throwable:上一任務傳入的異常
  • R:返回的對象類型

handle 和 thenApply 的區別:如果任務出現異常不會進入 thenApply;任務出現異常也會進入 handle,可對異常處理。

handle 和 whenComplete 的區別:handle 可對傳入值 T 進行轉換,并產生自己的返回結果 R;whenComplete 的返回值和上級任務傳入的結果一致,不能轉換。

whenComplete、whenCompleteAsync、handle 和 handleAsync 的輸入參數一個是正常結果一個是異常結果,而 exceptionally 的輸入參數為異常結果。

public static void main(String[] args) throws Exception {

    var supplyAsyncTask = CompletableFuture.supplyAsync(() -> {
        // 制造一個異常
        // int value = 1 / 0;
        return "supplyAsyncTask";
    });

    var handle = supplyAsyncTask.handle((s, throwable) -> {
        if (Optional.ofNullable(throwable).isPresent()) {
            return throwable.getMessage();
        }
        return new ArrayList() {{
            add(s);
        }};
    });

    // supplyAsyncTask異常時,輸出1:java.lang.ArithmeticException: / by zero
    // 輸出2:[supplyAsyncTask]
    System.out.println(handle.get());
}

exceptionally

某個任務執行拋出異常時執行的回調方法。拋出異常作為參數,傳遞到回調方法。僅處理異常情況。如果任務成功完成,那么將被跳過。

public static void main(String[] args) throws Exception {

    var supplyAsyncTask = CompletableFuture.supplyAsync(() -> {
        double error=1/0;
        return "ok";
    }, executor).exceptionally((e)->{
        // java.lang.ArithmeticException: / by zero
        System.out.println(e.getMessage());
        return "error";
    });

    // "error"
    System.out.println(supplyAsyncTask.get());
}

complete

如果尚未完成,則將 get() 和相關方法返回的值設置為給定值。如果此調用導致此 CompletableFuture 轉換到完成狀態,則返回 true,否則返回 false。文檔描述:

If not already completed, sets the value returned by get() and related methods to the given value.

Params:
value – the result value
Returns:
true if this invocation caused this CompletableFuture to transition to a completed state, else false
public static void main(String[] args) throws Exception {
    var task1 = CompletableFuture.supplyAsync(() -> {
        try {
            TimeUnit.SECONDS.sleep(15);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return 10;
    });

    // 若get放在此處,一直等待task1完成,輸出10
    // System.out.println(task1.get());
    // 強制task1完成,輸出true
    System.out.println(task1.complete(5));
    // 輸出5
    System.out.println(task1.get());
    // task1已完成,輸出false
    System.out.println(task1.complete(50));
}

thenCompose

源碼定義

public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) ;
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor) ;

thenCompose 方法會在某個任務執行完成后,將該任務的執行結果作為入參,執行指定的方法。該方法會返回一個新的 CompletableFuture 實例

public static void main(String[] args) throws Exception {
    var task1 = CompletableFuture.supplyAsync(() -> 10);

    var task2 = task1.thenCompose(param -> {
        System.out.println("this is task2 param=" + param);
        return CompletableFuture.supplyAsync(() -> {
            System.out.println("this is task2 square");
            return Math.pow(param, 2);
        });
    }).thenApply(param -> {
        System.out.println("thenApply get the square=" + param);
        return param;
    });

    var task3 = task1.thenCompose(param -> {
        System.out.println("this is task3 param=" + param);
        return CompletableFuture.runAsync(() -> {
            System.out.println("this is task3 square");
            System.out.println(Math.pow(param, 2));
        });
    });

    System.out.println("task2 get=" + task2.get());
    System.out.println("task3 get=" + task3.get());
}

輸出:
this is task2 param=10
this is task2 square
thenApply get the square=100.0
this is task3 param=10
this is task3 square
100.0
task2 get=100.0
task3 get=null

allOf

靜態方法,阻塞等待所有給定的 CompletableFuture 執行結束后,返回一個 CompletableFuture<Void> 結果。所有任務都執行完成后,才執行 allOf 的回調方法。如果任意一個任務異常,執行 get 方法時會拋出異常。

public static void main(String[] args) throws Exception {

    var task1 = CompletableFuture.supplyAsync(() -> {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "task1";
    });

    var task2 = CompletableFuture.runAsync(() -> {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        int value = 1 / 0;
        System.out.println("task2 is over");
    });

    CompletableFuture.allOf(task1, task2).whenComplete((param, throwable) -> {
        // null
        System.out.println(param);
    }).exceptionally(throwable -> {
        // task3 allOf throwable=java.lang.ArithmeticException: / by zero
        System.out.println("task3 allOf throwable=" + throwable.getMessage());
        return null;
    }).get();
}

anyOf

靜態方法,阻塞等待任意一個給定的 CompletableFuture 對象執行結束后,返回一個 CompletableFuture<Void> 結果。任意一個任務執行完,就執行 anyOf 的回調方法。如果執行的任務異常,執行 get 方法時會拋出異常。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容