CompletableFuture 示例

Java 8 有大量的新特性和增強(qiáng)如 Lambda 表達(dá)式锹杈,Streams撵孤,CompletableFuture等。在本篇文章中我將詳細(xì)解釋清楚CompletableFuture以及它所有方法的使用竭望,API介紹參見CompletableFuture相關(guān)API

什么是CompletableFuture邪码?

在Java中CompletableFuture用于異步編程,異步編程是編寫非阻塞的代碼咬清,運(yùn)行的任務(wù)在一個(gè)單獨(dú)的線程闭专,與主線程隔離,并且會(huì)通知主線程它的進(jìn)度旧烧,成功或者失敗影钉。

在這種方式中,主線程不會(huì)被阻塞掘剪,不需要一直等到子線程完成平委。主線程可以并行的執(zhí)行其他任務(wù)。

使用這種并行方式夺谁,可以極大的提高程序的性能廉赔。

Future vs CompletableFuture

CompletableFuture 是 Future API的擴(kuò)展。

Future 被用于作為一個(gè)異步計(jì)算結(jié)果的引用匾鸥。提供一個(gè) isDone() 方法來檢查計(jì)算任務(wù)是否完成蜡塌。當(dāng)任務(wù)完成時(shí),get()方法用來接收計(jì)算任務(wù)的結(jié)果勿负。

Callbale和 Future 教程可以學(xué)習(xí)更多關(guān)于 Future 知識(shí).

Future API 是非常好的 Java 異步編程進(jìn)階馏艾,但是它缺乏一些非常重要和有用的特性。

Future 的局限性

  1. 不能手動(dòng)完成 當(dāng)你寫了一個(gè)函數(shù)笆环,用于通過一個(gè)遠(yuǎn)程API獲取一個(gè)電子商務(wù)產(chǎn)品最新價(jià)格攒至。因?yàn)檫@個(gè) API 太耗時(shí)厚者,你把它允許在一個(gè)獨(dú)立的線程中躁劣,并且從你的函數(shù)中返回一個(gè) Future。 現(xiàn)在假設(shè)這個(gè)API服務(wù)宕機(jī)了库菲,這時(shí)你想通過該產(chǎn)品的最新緩存價(jià)格手工完成這個(gè)Future 账忘。你會(huì)發(fā)現(xiàn)無法這樣做。
  2. Future 的結(jié)果在非阻塞的情況下熙宇,不能執(zhí)行更進(jìn)一步的操作 Future 不會(huì)通知你它已經(jīng)完成了鳖擒,它提供了一個(gè)阻塞的 get() 方法通知你結(jié)果。 你無法給 Future 植入一個(gè)回調(diào)函數(shù)烫止,當(dāng) Future 結(jié)果可用的時(shí)候蒋荚,用該回調(diào)函數(shù)自動(dòng)的調(diào)用 Future 的結(jié)果。
  3. 多個(gè) Future 不能串聯(lián)在一起組成鏈?zhǔn)秸{(diào)用 有時(shí)候你需要執(zhí)行一個(gè)長時(shí)間運(yùn)行的計(jì)算任務(wù)馆蠕,并且當(dāng)計(jì)算任務(wù)完成的時(shí)候期升,你需要把它的計(jì)算結(jié)果發(fā)送給另外一個(gè)長時(shí)間運(yùn)行的計(jì)算任務(wù)等等惊奇。 你會(huì)發(fā)現(xiàn)你無法使用 Future 創(chuàng)建這樣的一個(gè)工作流。
  4. 不能組合多個(gè) Future 的結(jié)果 假設(shè)你有10個(gè)不同的Future播赁,你想并行的運(yùn)行颂郎,然后在它們運(yùn)行未完成后運(yùn)行一些函數(shù)。你會(huì)發(fā)現(xiàn)你也無法使用 Future 這樣做容为。
  5. 沒有異常處理 Future API 沒有任務(wù)的異常處理結(jié)構(gòu)

居然有如此多的限制乓序,幸好我們有CompletableFuture,你可以使用 CompletableFuture 達(dá)到以上所有目的坎背。

CompletableFuture 實(shí)現(xiàn)了 FutureCompletionStage接口替劈,并且提供了許多關(guān)于創(chuàng)建,鏈?zhǔn)秸{(diào)用和組合多個(gè) Future 的便利方法集沼瘫,而且有廣泛的異常處理支持抬纸。

創(chuàng)建 CompletableFuture

1. 簡單的例子
可以使用如下無參構(gòu)造函數(shù)簡單的創(chuàng)建 CompletableFuture:

CompletableFuture<String> completableFuture = new CompletableFuture<String>();

這是一個(gè)最簡單的 CompletableFuture,想獲取CompletableFuture 的結(jié)果可以使用 CompletableFuture.get() 方法:

String result = completableFuture.get()

get() 方法會(huì)一直阻塞直到 Future 完成耿戚。因此湿故,以上的調(diào)用將被永遠(yuǎn)阻塞,因?yàn)樵揊uture一直不會(huì)完成膜蛔。

你可以使用 CompletableFuture.complete() 手工的完成一個(gè) Future:

completableFuture.complete("Future's Result")

所有等待這個(gè) Future 的客戶端都將得到一個(gè)指定的結(jié)果坛猪,并且 completableFuture.complete() 之后的調(diào)用將被忽略。

2. 使用 runAsync() 運(yùn)行異步計(jì)算
如果你想異步的運(yùn)行一個(gè)后臺(tái)任務(wù)并且不想改任務(wù)返回任務(wù)東西皂股,這時(shí)候可以使用 CompletableFuture.runAsync()方法墅茉,它持有一個(gè)Runnable 對(duì)象,并返回 CompletableFuture<Void>呜呐。

// Run a task specified by a Runnable Object asynchronously.
CompletableFuture<Void> future = CompletableFuture.runAsync(new Runnable() {
    @Override
    public void run() {
        // Simulate a long-running Job
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }
        System.out.println("I'll run in a separate thread than the main thread.");
    }
});

// Block and wait for the future to complete
future.get()

你也可以以 lambda 表達(dá)式的形式傳入 Runnable 對(duì)象:

// Using Lambda Expression
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    // Simulate a long-running Job   
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    }
    System.out.println("I'll run in a separate thread than the main thread.");
});

在本文中就斤,我使用lambda表達(dá)式會(huì)比較頻繁,如果以前你沒有使用過蘑辑,建議你也多使用lambda 表達(dá)式洋机。

3. 使用 supplyAsync() 運(yùn)行一個(gè)異步任務(wù)并且返回結(jié)果
當(dāng)任務(wù)不需要返回任何東西的時(shí)候, CompletableFuture.runAsync() 非常有用洋魂。但是如果你的后臺(tái)任務(wù)需要返回一些結(jié)果應(yīng)該要怎么樣绷旗?

CompletableFuture.supplyAsync() 就是你的選擇。它持有supplier<T> 并且返回CompletableFuture<T>副砍,T 是通過調(diào)用 傳入的supplier取得的值的類型衔肢。

// Run a task specified by a Supplier object asynchronously
CompletableFuture<String> future = CompletableFuture.supplyAsync(new Supplier<String>() {
    @Override
    public String get() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }
        return "Result of the asynchronous computation";
    }
});

// Block and get the result of the Future
String result = future.get();
System.out.println(result);

Supplier<T> 是一個(gè)簡單的函數(shù)式接口,表示supplier的結(jié)果豁翎。它有一個(gè)get()方法角骤,該方法可以寫入你的后臺(tái)任務(wù)中,并且返回結(jié)果心剥。

你可以使用lambda表達(dá)式使得上面的示例更加簡明:

// Using Lambda Expression
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    }
    return "Result of the asynchronous computation";
});

一個(gè)關(guān)于Executor 和Thread Pool筆記
你可能想知道邦尊,我們知道runAsync()supplyAsync()方法在單獨(dú)的線程中執(zhí)行他們的任務(wù)硼控。但是我們不會(huì)永遠(yuǎn)只創(chuàng)建一個(gè)線程。
CompletableFuture可以從全局的 ForkJoinPool.commonPool()獲得一個(gè)線程中執(zhí)行這些任務(wù)胳赌。
但是你也可以創(chuàng)建一個(gè)線程池并傳給runAsync()supplyAsync()方法來讓他們從線程池中獲取一個(gè)線程執(zhí)行它們的任務(wù)牢撼。
CompletableFuture API 的所有方法都有兩個(gè)變體-一個(gè)接受Executor作為參數(shù),另一個(gè)不這樣:

// Variations of runAsync() and supplyAsync() methods
static CompletableFuture<Void>  runAsync(Runnable runnable)
static CompletableFuture<Void>  runAsync(Runnable runnable, Executor executor)
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

創(chuàng)建一個(gè)線程池疑苫,并傳遞給其中一個(gè)方法:

Executor executor = Executors.newFixedThreadPool(10);
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    }
    return "Result of the asynchronous computation";
}, executor);

在 CompletableFuture 轉(zhuǎn)換和運(yùn)行

CompletableFuture.get()方法是阻塞的熏版。它會(huì)一直等到Future完成并且在完成后返回結(jié)果。
但是捍掺,這是我們想要的嗎撼短?對(duì)于構(gòu)建異步系統(tǒng),我們應(yīng)該附上一個(gè)回調(diào)給CompletableFuture挺勿,當(dāng)Future完成的時(shí)候曲横,自動(dòng)的獲取結(jié)果。
如果我們不想等待結(jié)果返回不瓶,我們可以把需要等待Future完成執(zhí)行的邏輯寫入到回調(diào)函數(shù)中禾嫉。

可以使用 thenApply(), thenAccept()thenRun()方法附上一個(gè)回調(diào)給CompletableFuture。

1. thenApply()
可以使用 thenApply() 處理和改變CompletableFuture的結(jié)果蚊丐。持有一個(gè)Function<R,T>作為參數(shù)熙参。Function<R,T>是一個(gè)簡單的函數(shù)式接口,接受一個(gè)T類型的參數(shù)麦备,產(chǎn)出一個(gè)R類型的結(jié)果孽椰。

// Create a CompletableFuture
CompletableFuture<String> whatsYourNameFuture = CompletableFuture.supplyAsync(() -> {
   try {
       TimeUnit.SECONDS.sleep(1);
   } catch (InterruptedException e) {
       throw new IllegalStateException(e);
   }
   return "Rajeev";
});

// Attach a callback to the Future using thenApply()
CompletableFuture<String> greetingFuture = whatsYourNameFuture.thenApply(name -> {
   return "Hello " + name;
});

// Block and get the result of the future.
System.out.println(greetingFuture.get()); // Hello Rajeev

你也可以通過附加一系列的thenApply()在回調(diào)方法 在CompletableFuture寫一個(gè)連續(xù)的轉(zhuǎn)換。這樣的話凛篙,結(jié)果中的一個(gè) thenApply方法就會(huì)傳遞給該系列的另外一個(gè) thenApply方法黍匾。

CompletableFuture<String> welcomeText = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
       throw new IllegalStateException(e);
    }
    return "Rajeev";
}).thenApply(name -> {
    return "Hello " + name;
}).thenApply(greeting -> {
    return greeting + ", Welcome to the CalliCoder Blog";
});

System.out.println(welcomeText.get());
// Prints - Hello Rajeev, Welcome to the CalliCoder Blog

2. thenAccept() 和 thenRun()
如果你不想從你的回調(diào)函數(shù)中返回任何東西,僅僅想在Future完成后運(yùn)行一些代碼片段呛梆,你可以使用thenAccept()thenRun()方法锐涯,這些方法經(jīng)常在調(diào)用鏈的最末端的最后一個(gè)回調(diào)函數(shù)中使用。
CompletableFuture.thenAccept()持有一個(gè)Consumer<T>削彬,返回一個(gè)CompletableFuture<Void>全庸。它可以訪問CompletableFuture的結(jié)果:

// thenAccept() example
CompletableFuture.supplyAsync(() -> {
    return ProductService.getProductDetail(productId);
}).thenAccept(product -> {
    System.out.println("Got product detail from remote service " + product.getName())
});

雖然thenAccept()可以訪問CompletableFuture的結(jié)果秀仲,但thenRun()不能訪Future的結(jié)果融痛,它持有一個(gè)Runnable返回CompletableFuture<void >

// thenRun() example
CompletableFuture.supplyAsync(() -> {
    // Run some computation  
}).thenRun(() -> {
    // Computation Finished.
});

異步回調(diào)方法的筆記
CompletableFuture提供的所有回調(diào)方法都有兩個(gè)變體:
// thenApply() variants <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn) <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
這些異步回調(diào)變體通過在獨(dú)立的線程中執(zhí)行回調(diào)任務(wù)幫助你進(jìn)一步執(zhí)行并行計(jì)算。
以下示例:

CompletableFuture.supplyAsync(() -> {
    try {
       TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
      throw new IllegalStateException(e);
    }
    return "Some Result"
}).thenApply(result -> {
    /* 
      Executed in the same thread where the supplyAsync() task is executed
      or in the main thread If the supplyAsync() task completes immediately (Remove sleep() call to verify)
    */
    return "Processed Result"
})

在以上示例中神僵,在thenApply()中的任務(wù)和在supplyAsync()中的任務(wù)執(zhí)行在相同的線程中雁刷。任何supplyAsync()立即執(zhí)行完成,那就是執(zhí)行在主線程中(嘗試刪除sleep測試下)。
為了控制執(zhí)行回調(diào)任務(wù)的線程保礼,你可以使用異步回調(diào)沛励。如果你使用thenApplyAsync()回調(diào)责语,將從ForkJoinPool.commonPool()獲取不同的線程執(zhí)行。

CompletableFuture.supplyAsync(() -> {
    return "Some Result"
}).thenApplyAsync(result -> {
    // Executed in a different thread from ForkJoinPool.commonPool()
    return "Processed Result"
})

此外目派,如果你傳入一個(gè)ExecutorthenApplyAsync()回調(diào)中坤候,,任務(wù)將從Executor線程池獲取一個(gè)線程執(zhí)行企蹭。

Executor executor = Executors.newFixedThreadPool(2);
CompletableFuture.supplyAsync(() -> {
    return "Some result"
}).thenApplyAsync(result -> {
    // Executed in a thread obtained from the executor
    return "Processed Result"
}, executor);

組合兩個(gè)CompletableFuture

1. 使用 thenCompose()組合兩個(gè)獨(dú)立的future
假設(shè)你想從一個(gè)遠(yuǎn)程API中獲取一個(gè)用戶的詳細(xì)信息白筹,一旦用戶信息可用,你想從另外一個(gè)服務(wù)中獲取他的貸方谅摄。
考慮下以下兩個(gè)方法getUserDetail()getCreditRating()的實(shí)現(xiàn):

CompletableFuture<User> getUsersDetail(String userId) {
    return CompletableFuture.supplyAsync(() -> {
        UserService.getUserDetails(userId);
    }); 
}

CompletableFuture<Double> getCreditRating(User user) {
    return CompletableFuture.supplyAsync(() -> {
        CreditRatingService.getCreditRating(user);
    });
}

現(xiàn)在讓我們弄明白當(dāng)使用了thenApply()后是否會(huì)達(dá)到我們期望的結(jié)果-

CompletableFuture<CompletableFuture<Double>> result = getUserDetail(userId)
.thenApply(user -> getCreditRating(user));

在更早的示例中徒河,Supplier函數(shù)傳入thenApply將返回一個(gè)簡單的值,但是在本例中送漠,將返回一個(gè)CompletableFuture顽照。以上示例的最終結(jié)果是一個(gè)嵌套的CompletableFuture。
如果你想獲取最終的結(jié)果給最頂層future闽寡,使用 thenCompose()方法代替-

CompletableFuture<Double> result = getUserDetail(userId)
.thenCompose(user -> getCreditRating(user));

因此代兵,規(guī)則就是-如果你的回調(diào)函數(shù)返回一個(gè)CompletableFuture,但是你想從CompletableFuture鏈中獲取一個(gè)直接合并后的結(jié)果爷狈,這時(shí)候你可以使用thenCompose()奢人。

2. 使用thenCombine()組合兩個(gè)獨(dú)立的 future
雖然thenCompose()被用于當(dāng)一個(gè)future依賴另外一個(gè)future的時(shí)候用來組合兩個(gè)future。thenCombine()被用來當(dāng)兩個(gè)獨(dú)立的Future都完成的時(shí)候淆院,用來做一些事情何乎。

System.out.println("Retrieving weight.");
CompletableFuture<Double> weightInKgFuture = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
       throw new IllegalStateException(e);
    }
    return 65.0;
});

System.out.println("Retrieving height.");
CompletableFuture<Double> heightInCmFuture = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
       throw new IllegalStateException(e);
    }
    return 177.8;
});

System.out.println("Calculating BMI.");
CompletableFuture<Double> combinedFuture = weightInKgFuture
        .thenCombine(heightInCmFuture, (weightInKg, heightInCm) -> {
    Double heightInMeter = heightInCm/100;
    return weightInKg/(heightInMeter*heightInMeter);
});

System.out.println("Your BMI is - " + combinedFuture.get());

當(dāng)兩個(gè)Future都完成的時(shí)候,傳給``thenCombine()的回調(diào)函數(shù)將被調(diào)用土辩。

組合多個(gè)CompletableFuture

我們使用thenCompose()thenCombine()把兩個(gè)CompletableFuture組合在一起≈Ь龋現(xiàn)在如果你想組合任意數(shù)量的CompletableFuture,應(yīng)該怎么做拷淘?我們可以使用以下兩個(gè)方法組合任意數(shù)量的CompletableFuture各墨。

static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)

1. CompletableFuture.allOf()
CompletableFuture.allOf的使用場景是當(dāng)你一個(gè)列表的獨(dú)立future,并且你想在它們都完成后并行的做一些事情启涯。

假設(shè)你想下載一個(gè)網(wǎng)站的100個(gè)不同的頁面贬堵。你可以串行的做這個(gè)操作,但是這非常消耗時(shí)間结洼。因此你想寫一個(gè)函數(shù)黎做,傳入一個(gè)頁面鏈接,返回一個(gè)CompletableFuture松忍,異步的下載頁面內(nèi)容蒸殿。

CompletableFuture<String> downloadWebPage(String pageLink) {
    return CompletableFuture.supplyAsync(() -> {
        // Code to download and return the web page's content
    });
} 

現(xiàn)在,當(dāng)所有的頁面已經(jīng)下載完畢,你想計(jì)算包含關(guān)鍵字CompletableFuture頁面的數(shù)量宏所∷盅蓿可以使用CompletableFuture.allOf()達(dá)成目的。

List<String> webPageLinks = Arrays.asList(...)  // A list of 100 web page links

// Download contents of all the web pages asynchronously
List<CompletableFuture<String>> pageContentFutures = webPageLinks.stream()
        .map(webPageLink -> downloadWebPage(webPageLink))
        .collect(Collectors.toList());

// Create a combined Future using allOf()
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
        pageContentFutures.toArray(new CompletableFuture[pageContentFutures.size()])
);

使用CompletableFuture.allOf()的問題是它返回CompletableFuture<void>爬骤。但是我們可以通過寫一些額外的代碼來獲取所有封裝的CompletableFuture結(jié)果充石。</void>

// When all the Futures are completed, call `future.join()` to get their results and collect the results in a list -
CompletableFuture<List<String>> allPageContentsFuture = allFutures.thenApply(v -> {
   return pageContentFutures.stream()
           .map(pageContentFuture -> pageContentFuture.join())
           .collect(Collectors.toList());
});

花一些時(shí)間理解下以上代碼片段。當(dāng)所有future完成的時(shí)候霞玄,我們調(diào)用了future.join()赫冬,因此我們不會(huì)在任何地方阻塞。

join()方法和get()方法非常類似溃列,這唯一不同的地方是如果最頂層的CompletableFuture完成的時(shí)候發(fā)生了異常劲厌,它會(huì)拋出一個(gè)未經(jīng)檢查的異常。

現(xiàn)在讓我們計(jì)算包含關(guān)鍵字頁面的數(shù)量听隐。

// Count the number of web pages having the "CompletableFuture" keyword.
CompletableFuture<Long> countFuture = allPageContentsFuture.thenApply(pageContents -> {
    return pageContents.stream()
            .filter(pageContent -> pageContent.contains("CompletableFuture"))
            .count();
});

System.out.println("Number of Web Pages having CompletableFuture keyword - " + 
        countFuture.get());

2. CompletableFuture.anyOf()

CompletableFuture.anyOf()和其名字介紹的一樣补鼻,當(dāng)任何一個(gè)CompletableFuture完成的時(shí)候【相同的結(jié)果類型】,返回一個(gè)新的CompletableFuture雅任。以下示例:

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
       throw new IllegalStateException(e);
    }
    return "Result of Future 1";
});

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
       throw new IllegalStateException(e);
    }
    return "Result of Future 2";
});

CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
       throw new IllegalStateException(e);
    }
    return "Result of Future 3";
});

CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(future1, future2, future3);

System.out.println(anyOfFuture.get()); // Result of Future 2

在以上示例中风范,當(dāng)三個(gè)中的任何一個(gè)CompletableFuture完成, anyOfFuture就會(huì)完成沪么。因?yàn)?code>future2的休眠時(shí)間最少硼婿,因此她最先完成,最終的結(jié)果將是future2的結(jié)果禽车。

CompletableFuture.anyOf()傳入一個(gè)Future可變參數(shù)寇漫,返回CompletableFuture<object>。CompletableFuture.anyOf()的問題是如果你的CompletableFuture返回的結(jié)果是不同類型的殉摔,這時(shí)候你講會(huì)不知道你最終CompletableFuture是什么類型州胳。

CompletableFuture 異常處理

我們探尋了怎樣創(chuàng)建CompletableFuture,轉(zhuǎn)換它們逸月,并組合多個(gè)CompletableFuture∷ㄗ玻現(xiàn)在讓我們弄明白當(dāng)發(fā)生錯(cuò)誤的時(shí)候我們應(yīng)該怎么做。

首先讓我們明白在一個(gè)回調(diào)鏈中錯(cuò)誤是怎么傳遞的碗硬。思考下以下回調(diào)鏈:

CompletableFuture.supplyAsync(() -> {
    // Code which might throw an exception
    return "Some result";
}).thenApply(result -> {
    return "processed result";
}).thenApply(result -> {
    return "result after further processing";
}).thenAccept(result -> {
    // do something with the final result
});

如果在原始的supplyAsync()任務(wù)中發(fā)生一個(gè)錯(cuò)誤瓤湘,這時(shí)候沒有任何thenApply會(huì)被調(diào)用并且future將以一個(gè)異常結(jié)束。如果在第一個(gè)thenApply發(fā)生錯(cuò)誤恩尾,這時(shí)候第二個(gè)和第三個(gè)將不會(huì)被調(diào)用弛说,同樣的,future將以異常結(jié)束特笋。

1. 使用 exceptionally() 回調(diào)處理異常
exceptionally()回調(diào)給你一個(gè)從原始Future中生成的錯(cuò)誤恢復(fù)的機(jī)會(huì)剃浇。你可以在這里記錄這個(gè)異常并返回一個(gè)默認(rèn)值。

Integer age = -1;

CompletableFuture<String> maturityFuture = CompletableFuture.supplyAsync(() -> {
    if(age < 0) {
        throw new IllegalArgumentException("Age can not be negative");
    }
    if(age > 18) {
        return "Adult";
    } else {
        return "Child";
    }
}).exceptionally(ex -> {
    System.out.println("Oops! We have an exception - " + ex.getMessage());
    return "Unknown!";
});

System.out.println("Maturity : " + maturityFuture.get()); 

2. 使用 handle() 方法處理異常
API提供了一個(gè)更通用的方法 - handle()從異沉晕铮恢復(fù)虎囚,無論一個(gè)異常是否發(fā)生它都會(huì)被調(diào)用。

Integer age = -1;

CompletableFuture<String> maturityFuture = CompletableFuture.supplyAsync(() -> {
    if(age < 0) {
        throw new IllegalArgumentException("Age can not be negative");
    }
    if(age > 18) {
        return "Adult";
    } else {
        return "Child";
    }
}).handle((res, ex) -> {
    if(ex != null) {
        System.out.println("Oops! We have an exception - " + ex.getMessage());
        return "Unknown!";
    }
    return res;
});

System.out.println("Maturity : " + maturityFuture.get());

如果異常發(fā)生蔫磨,res參數(shù)將是 null淘讥,否則,ex將是 null堤如。
3. 使用 whenComplete() 方法處理異常
handle和exceptionally的入?yún)⑹荈untcion蒲列,whenComplete是consumer
public CompletableFuture<T> whenComplete( BiConsumer<? super T, ? super Throwable> action)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市搀罢,隨后出現(xiàn)的幾起案子蝗岖,更是在濱河造成了極大的恐慌,老刑警劉巖榔至,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抵赢,死亡現(xiàn)場離奇詭異,居然都是意外死亡唧取,警方通過查閱死者的電腦和手機(jī)铅鲤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來枫弟,“玉大人邢享,你說我怎么就攤上這事〉” “怎么了骇塘?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長韩容。 經(jīng)常有香客問我绪爸,道長,這世上最難降的妖魔是什么宙攻? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任奠货,我火速辦了婚禮,結(jié)果婚禮上座掘,老公的妹妹穿的比我還像新娘递惋。我一直安慰自己,他們只是感情好溢陪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布萍虽。 她就那樣靜靜地躺著,像睡著了一般形真。 火紅的嫁衣襯著肌膚如雪杉编。 梳的紋絲不亂的頭發(fā)上超全,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音邓馒,去河邊找鬼嘶朱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛光酣,可吹牛的內(nèi)容都是我干的疏遏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼救军,長吁一口氣:“原來是場噩夢啊……” “哼财异!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起唱遭,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤戳寸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后拷泽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庆揩,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年跌穗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了订晌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚌吸,死狀恐怖锈拨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情羹唠,我是刑警寧澤奕枢,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站佩微,受9級(jí)特大地震影響缝彬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜哺眯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一谷浅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奶卓,春花似錦一疯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至盏浙,卻和暖如春眉睹,著一層夾襖步出監(jiān)牢的瞬間荔茬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來泰國打工竹海, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留慕蔚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓站削,卻偏偏與公主長得像坊萝,于是被迫代替她去往敵國和親孵稽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子许起,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容