java之使用CompletableFuture入門2

快乐的总统95發表於2024-10-07

Java 17

-

序章

本文介紹用過的 allOf、anyOf 函式的用法。

allOf 函式原型

java之使用CompletableFuture入門2

兩點:

1、沒有返回值。

2、引數 cfs 中 任何一個 都不能是 null。

anyOf 函式原型

java之使用CompletableFuture入門2

兩點:

1、有返回值,為 Object。

2、引數 cfs 中 任何一個 都不能是 null。

allOf

測試意圖:

多個任務正常執行。ben釋出於部落格園

任務中任何一個發生異常。

測試程式碼:

// 呼叫:
// testAllOf(false);
// testAllOf(true);
// 
    /**
     * 測試 allOf 函式
     * @param makeEx 製造異常
     */
    private static void testAllOf(boolean makeEx) {
        printWithTime("allOf 0: makeEx=" + makeEx);
        CompletableFuture<String>[] arr = new CompletableFuture[4];
        IntStream.range(1,4).forEach(i->{
            arr[i-1] = CompletableFuture.supplyAsync(()->{
                var tn = Thread.currentThread().getName();
                printWithTime("start: " + tn + ", i=" + i);
                try {
                    // 2 4 6
                    TimeUnit.SECONDS.sleep(i * 2);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                printWithTime("end: " + tn + ", i=" + i);
                return "t#" + i;
            });
        });

        int len = 3;

        // 除錯:發生異常
        // 開關1:第4個是否發生異常
//        arr[3] = CompletableFuture.supplyAsync(()->{
//            var tn = Thread.currentThread().getName();
//            printWithTime("start: " + tn + ", i=OUT");
//
//            if (makeEx) {
//                throw new RuntimeException("發生了異常...boom");
//            }
//
//            try {
//                TimeUnit.SECONDS.sleep(5);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//            printWithTime("end: " + tn + ", i=OUT");
//            return "t#OUT";
//        });
//        len = 4;

        printWithTime("allOf 1");
        CompletableFuture<Void> allTasks = CompletableFuture.allOf(Arrays.copyOf(arr, len));
        printWithTime("allOf 2");

        try {
            allTasks.get();
            printWithTime("allOf 3");
        } catch (InterruptedException e) {
            printWithTime("e 1");
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            printWithTime("e 2");
            // 開關2:
            throw new RuntimeException(e);
        }

        printWithTime("allOf 4");
        for (CompletableFuture<String> cfs : arr) {
            if (Objects.nonNull(cfs)) {
                try {
                    printWithTime("result="+ cfs.get());
                } catch (InterruptedException e) {
                    printWithTime("e 3");
                    throw new RuntimeException(e);
                } catch (ExecutionException e) {
                    printWithTime("e 4");
                    throw new RuntimeException(e);
                }
            } else {
                printWithTime("cfs is null");
            }
        }

        printWithTime("allOf end.");
    }

所有執行緒正常執行

呼叫 testAllOf(false) 的結果

三個執行緒在執行,都順利執行了,分別等待了2、4、6秒。ben釋出於部落格園

所有執行完畢後,一起輸出了結果。

[2024-10-06 20:45:41] allOf 0: makeEx=false
[2024-10-06 20:45:41] allOf 1
[2024-10-06 20:45:41] allOf 2
[2024-10-06 20:45:41] start: ForkJoinPool.commonPool-worker-3, i=3
[2024-10-06 20:45:41] start: ForkJoinPool.commonPool-worker-2, i=2
[2024-10-06 20:45:41] start: ForkJoinPool.commonPool-worker-1, i=1
[2024-10-06 20:45:43] end: ForkJoinPool.commonPool-worker-1, i=1
[2024-10-06 20:45:45] end: ForkJoinPool.commonPool-worker-2, i=2
[2024-10-06 20:45:47] end: ForkJoinPool.commonPool-worker-3, i=3
[2024-10-06 20:45:47] allOf 3
[2024-10-06 20:45:47] allOf 4
[2024-10-06 20:45:47] result=t#1
[2024-10-06 20:45:47] result=t#2
[2024-10-06 20:45:47] result=t#3
[2024-10-06 20:45:47] cfs is null
[2024-10-06 20:45:47] allOf end.

執行緒執行發生異常

操作:開啟 開關1,給陣列新增第4給任務。ben釋出於部落格園

執行結果:

前3個正常執行了,第4個發生異常。

allOf 會等到 所有執行緒 都有結果——正常的 或 異常的。

allOf 中 任何一個發生異常時,allOf 發生異常

[2024-10-06 20:53:29] allOf 0: makeEx=true
[2024-10-06 20:53:29] allOf 1
[2024-10-06 20:53:29] allOf 2
[2024-10-06 20:53:29] start: ForkJoinPool.commonPool-worker-4, i=OUT
[2024-10-06 20:53:29] start: ForkJoinPool.commonPool-worker-2, i=2
[2024-10-06 20:53:29] start: ForkJoinPool.commonPool-worker-1, i=1
[2024-10-06 20:53:29] start: ForkJoinPool.commonPool-worker-3, i=3
[2024-10-06 20:53:31] end: ForkJoinPool.commonPool-worker-1, i=1
[2024-10-06 20:53:33] end: ForkJoinPool.commonPool-worker-2, i=2
[2024-10-06 20:53:35] end: ForkJoinPool.commonPool-worker-3, i=3
[2024-10-06 20:53:35] e 2
Exception in thread "main" java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.RuntimeException: 發生了異常...boom

上面在 e 2 發生異常,但是,throw 了異常,如果不丟擲異常呢?

操作:關閉 開關2——註釋掉 丟擲異常

執行結果:

e 2 後可以繼續執行。

執行到 第4個發生異常的執行緒時,丟擲異常。ben釋出於部落格園

[2024-10-06 20:58:01] allOf 0: makeEx=true
[2024-10-06 20:58:01] allOf 1
[2024-10-06 20:58:01] allOf 2
[2024-10-06 20:58:01] start: ForkJoinPool.commonPool-worker-4, i=OUT
[2024-10-06 20:58:01] start: ForkJoinPool.commonPool-worker-2, i=2
[2024-10-06 20:58:01] start: ForkJoinPool.commonPool-worker-1, i=1
[2024-10-06 20:58:01] start: ForkJoinPool.commonPool-worker-3, i=3
[2024-10-06 20:58:03] end: ForkJoinPool.commonPool-worker-1, i=1
[2024-10-06 20:58:05] end: ForkJoinPool.commonPool-worker-2, i=2
[2024-10-06 20:58:07] end: ForkJoinPool.commonPool-worker-3, i=3
[2024-10-06 20:58:07] e 2
[2024-10-06 20:58:07] allOf 4
[2024-10-06 20:58:07] result=t#1
[2024-10-06 20:58:07] result=t#2
[2024-10-06 20:58:07] result=t#3
[2024-10-06 20:58:07] e 4
Exception in thread "main" java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.RuntimeException: 發生了異常...boom

處理異常的方式

當然,可以用 exceptionally(...) 函式來處理 異常,避免異常傳到外面的處理流程中。

anyOf

任何一個 任務執行完,立即返回其結果——正常的、異常的。ben釋出於部落格園

注意,是 有返回值 的。

正常執行1

將 上面測試程式碼的 allOf 替換為 anyOf 進行測試。程式碼:

    /**
     * 測試 anyOf 函式
     * @param makeEx 製造異常
     */
    private static void testAnyOf(boolean makeEx) {
        printWithTime("anyOf 0: makeEx=" + makeEx);
        CompletableFuture<String>[] arr = new CompletableFuture[4];
        IntStream.range(1,4).forEach(i->{
            arr[i-1] = CompletableFuture.supplyAsync(()->{
                var tn = Thread.currentThread().getName();
                printWithTime("start: " + tn + ", i=" + i);
                try {
                    // 2 4 6
                    TimeUnit.SECONDS.sleep(i * 2);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                printWithTime("end: " + tn + ", i=" + i);
                return "t#" + i;
            });
        });

        int len = 3;

        // 除錯:發生異常
        // 開關1:第4個是否發生異常
//        arr[3] = CompletableFuture.supplyAsync(()->{
//            var tn = Thread.currentThread().getName();
//            printWithTime("start: " + tn + ", i=OUT");
//
//            if (makeEx) {
//                throw new RuntimeException("發生了異常...boom");
//            }
//
//            try {
//                TimeUnit.SECONDS.sleep(5);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//            printWithTime("end: " + tn + ", i=OUT");
//            return "t#OUT";
//        });
//        len = 4;

        printWithTime("anyOf 1");
        CompletableFuture<Object> allTasks = CompletableFuture.anyOf(Arrays.copyOf(arr, len));
        printWithTime("anyOf 2");

        try {
            Object x = allTasks.get();
            printWithTime("anyOf 3: x=" + x);
        } catch (InterruptedException e) {
            printWithTime("e 1");
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            printWithTime("e 2");
            // 開關2:
            throw new RuntimeException(e);
        }

        printWithTime("anyOf 4");
        for (CompletableFuture<String> cfs : arr) {
            if (Objects.nonNull(cfs)) {
                try {
                    printWithTime("result="+ cfs.get());
                } catch (InterruptedException e) {
                    printWithTime("e 3");
                    throw new RuntimeException(e);
                } catch (ExecutionException e) {
                    printWithTime("e 4");
                    throw new RuntimeException(e);
                }
            } else {
                printWithTime("cfs is null");
            }
        }

        printWithTime("anyOf end.");
    }

testAnyOf(false) 執行結果:ben釋出於部落格園

3個任務,休眠2 秒的先返回,anyOf get到結果了。

但是,其它2個任務 還是會繼續執行完,在後面的程式碼 get時,出現等待的情況。

當然,因為 各個任務的 休眠時間不同,總是 休眠最短的 返回。

[2024-10-06 21:10:37] anyOf 0: makeEx=false
[2024-10-06 21:10:37] anyOf 1
[2024-10-06 21:10:37] anyOf 2
[2024-10-06 21:10:37] start: ForkJoinPool.commonPool-worker-2, i=2
[2024-10-06 21:10:37] start: ForkJoinPool.commonPool-worker-3, i=3
[2024-10-06 21:10:37] start: ForkJoinPool.commonPool-worker-1, i=1
[2024-10-06 21:10:39] end: ForkJoinPool.commonPool-worker-1, i=1
[2024-10-06 21:10:39] anyOf 3: x=t#1
[2024-10-06 21:10:39] anyOf 4
[2024-10-06 21:10:39] result=t#1
[2024-10-06 21:10:41] end: ForkJoinPool.commonPool-worker-2, i=2
[2024-10-06 21:10:41] result=t#2
[2024-10-06 21:10:43] end: ForkJoinPool.commonPool-worker-3, i=3
[2024-10-06 21:10:43] result=t#3
[2024-10-06 21:10:43] cfs is null
[2024-10-06 21:10:43] anyOf end.
[2024-10-06 21:10:43] end main.

Process finished with exit code 0

正常執行:隨機結果

3個任務,全部休眠 4秒。ben釋出於部落格園

TimeUnit.SECONDS.sleep(4);

執行結果:

anyOf 3: x=t#1、2、3 都有可能,隨機的。

執行緒執行發生異常

在上一步的基礎上,開啟開關1——取消註釋。

休眠4秒後,丟擲異常(和之前不同):

// 除錯:發生異常
// 開關1:第4個是否發生異常
arr[3] = CompletableFuture.supplyAsync(()->{
    var tn = Thread.currentThread().getName();
    printWithTime("start: " + tn + ", i=OUT");

    try {
        TimeUnit.SECONDS.sleep(4);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }

    if (makeEx) {
        throw new RuntimeException("發生了異常...boom");
    }

    printWithTime("end: " + tn + ", i=OUT");
    return "t#OUT";
});
len = 4;
testAnyOf(true) 執行結果:

有時候結果是正常的,可以走到 e 4;有時候結果是異常的,走到 e 2 就結束了。

符合預期。ben釋出於部落格園

同 allOf 函式,可以用 exceptionally 函式來處理——異常時內部處理,不干擾外面流程。

小結

一個場景:

在獲取資料庫資料時,可以同時獲取多個,再在程式中進行計算。

---end---ben釋出於部落格園

本文連結:

https://www.cnblogs.com/luo630/p/18448304

參考資料

1、4.非同步程式設計利器:CompletableFuture詳解-Java開發實戰
2021-06-06
作者:撿田螺的小男孩
https://juejin.cn/post/6970558076642394142

2、CompletableFuture 組合處理 allOf 和 anyOf太讚了!
2021-05-31
作者:碼農架構
https://juejin.cn/post/6968286631614742535

3、

ben釋出於部落格園

ben釋出於部落格園

相關文章