Java 8 (10) CompletableFuture:组合式异步编程
隨著多核處理器的出現(xiàn),提升應(yīng)用程序的處理速度最有效的方式就是可以編寫(xiě)出發(fā)揮多核能力的軟件,我們已經(jīng)可以通過(guò)切分大型的任務(wù),讓每個(gè)子任務(wù)并行運(yùn)行,使用線程的方式,分支/合并框架(java 7) 和并行流(java 8)來(lái)實(shí)現(xiàn)。
現(xiàn)在很多大型的互聯(lián)網(wǎng)公司都對(duì)外提供了API服務(wù),比如百度的地圖,微博的新聞,天氣預(yù)報(bào)等等。很少有網(wǎng)站或網(wǎng)絡(luò)應(yīng)用匯以完全隔離的方式工作,而是采用混聚的方式:它會(huì)使用來(lái)自多個(gè)源的內(nèi)容,將這些內(nèi)容聚合在一起,方便用戶使用。
比如實(shí)現(xiàn)一個(gè)功能,你需要在微博中搜索某個(gè)新聞,然后根據(jù)當(dāng)前坐標(biāo)獲取天氣預(yù)報(bào)。這些調(diào)用第三方信息的時(shí)候,不想因?yàn)榈却阉餍侣剷r(shí),放棄對(duì)獲取天氣預(yù)報(bào)的處理,于是我們可以使用 分支/合并框架 及并行流 來(lái)并行處理,將他們切分為多個(gè)子操作,在多個(gè)不同的核、CPU甚至是機(jī)器上并行的執(zhí)行這些子操作。
相反,如果你想實(shí)現(xiàn)并發(fā),而不是并行,或者你的主要目標(biāo)是在同一個(gè)CPU上執(zhí)行幾個(gè)松耦合的任務(wù),充分利用CPU的核,讓其足夠忙碌,從而最大化程序的吞吐量,那么你其實(shí)真正想做的是避免因?yàn)榈却h(yuǎn)程服務(wù)的返回,或者對(duì)數(shù)據(jù)庫(kù)的查詢,而阻塞線程的執(zhí)行,浪費(fèi)寶貴的計(jì)算資源,因?yàn)檫@種等待時(shí)間可能會(huì)很長(zhǎng)。Future接口,尤其是它的新版實(shí)現(xiàn)CompletableFuture是處理這種情況的利器。
?
Future接口
Future接口在java 5中被引入,設(shè)計(jì)初衷是對(duì)將來(lái)某個(gè)時(shí)刻會(huì)發(fā)生的結(jié)果進(jìn)行建模。它建模了一種異步計(jì)算,返回一個(gè)執(zhí)行運(yùn)算結(jié)果的引用,當(dāng)運(yùn)算結(jié)束后,這個(gè)引用被返回給調(diào)用方。在Future中觸發(fā)那些可能會(huì)耗時(shí)的操作把調(diào)用線程解放出來(lái),讓它能繼續(xù)執(zhí)行其他工作,不用一直等待耗時(shí)的操作完成,比如:你拿了一袋子衣服到洗衣店去洗衣服,洗衣店會(huì)給你張發(fā)票,告訴你什么時(shí)候會(huì)洗好,然后你就可以去做其他的事了。Future的另一個(gè)優(yōu)點(diǎn)是它比更底層的Thread更容易使用。使用Future只需要講耗時(shí)的操作封裝在一個(gè)Callable對(duì)象中,再將它提交給ExecutorService就可以了。 Java 8之前使用Future的例子:
public static void main(String[] args) {//創(chuàng)建Executor-Service,通過(guò)他可以向線程池提交任務(wù)ExecutorService executor = Executors.newCachedThreadPool();//向executor-Service提交 Callable對(duì)象Future<Double> future = executor.submit(new Callable<Double>() {@Overridepublic Double call() throws Exception {//異步的方式執(zhí)行耗時(shí)的操作return doSomeLongComputation();}});//異步時(shí),做其他的事情 doSomethingElse();try{//獲取異步操作的結(jié)果,如果被阻塞,無(wú)法得到結(jié)果,那么最多等待1秒鐘之后退出Double result = future.get(1, TimeUnit.SECONDS);System.out.print(result);} catch (InterruptedException e) {System.out.print("計(jì)算拋出一個(gè)異常");} catch (ExecutionException e) {System.out.print("當(dāng)前線程在等待過(guò)程中被中斷");} catch (TimeoutException e) {System.out.print("future對(duì)象完成之前已過(guò)期");}}public static Double doSomeLongComputation() throws InterruptedException {Thread.sleep(1000);return 3 + 4.5;}public static void doSomethingElse(){System.out.print("else");}這種方式可以再ExecutorService以并發(fā)的方式調(diào)用另外一個(gè)線程執(zhí)行耗時(shí)的操作的同時(shí),去執(zhí)行一些其他任務(wù)。接著到已經(jīng)沒(méi)有任務(wù)運(yùn)行時(shí),調(diào)用它的get方法來(lái)獲取操作的結(jié)果,如果操作完成,就會(huì)返回結(jié)果,否則會(huì)阻塞你的線程,一直到操作完成,返回響應(yīng)的結(jié)果。
?
CompletableFuture
在java 8 中引入了CompletableFuture類,它實(shí)現(xiàn)了Future接口,使用了Lambda表達(dá)式以及流水線的思想,通過(guò)下面這個(gè)例子進(jìn)行學(xué)習(xí),比如:我們要做一個(gè)商品查詢,根據(jù)折扣來(lái)獲取價(jià)格。
public class Shop {public double getPrice(String product) throws InterruptedException {//查詢商品的數(shù)據(jù)庫(kù),或鏈接其他外部服務(wù)獲取折扣Thread.sleep(1000);return new Random().nextDouble() * product.charAt(0) + product.charAt(1);} }當(dāng)調(diào)用這個(gè)方法時(shí),它會(huì)阻塞進(jìn)程,等待事件完成。
將同步方法轉(zhuǎn)換成異步方法
public Future<Double> getPriceAsync(String product){//創(chuàng)建CompletableFuture對(duì)象CompletableFuture<Double> futurePrice = new CompletableFuture<>();new Thread (()->{try {//在另一個(gè)線程中執(zhí)行計(jì)算double price = getPrice(product);//需要長(zhǎng)時(shí)間計(jì)算的任務(wù)結(jié)束并得出結(jié)果時(shí),設(shè)置future的返回值 futurePrice.complete(price);} catch (InterruptedException e) {e.printStackTrace();}}).start();return futurePrice;}然后可以這樣調(diào)用:
System.out.println("begin");Future<Double> futurePrice = shop.getPriceAsync("ss");System.out.println("doSomething");System.out.println(futurePrice.get());System.out.println("end");
begin
doSomething
171.47509091822835
end
這個(gè)例子中,首先會(huì)調(diào)用接口 立即返回一個(gè)Future對(duì)象,在這種方式下,在查詢價(jià)格的同時(shí),還可以處理其他任務(wù)。最后所有的工作都已經(jīng)完成,然后再調(diào)用future的get方法。獲得Future中封裝的值,要么發(fā)生阻塞,直到該任務(wù)異步任務(wù)完成,期望的值能夠返回。
?
錯(cuò)誤處理
如果沒(méi)有意外,這個(gè)代碼工作的會(huì)非常正常。但是如果計(jì)算價(jià)格的過(guò)程中發(fā)生了錯(cuò)誤,那么get會(huì)永久的被阻塞。這時(shí)可以使用重載的get方法,讓它超過(guò)一個(gè)時(shí)間后就強(qiáng)制返回。應(yīng)該盡量在代碼中使用這種方式來(lái)防止程序永久的等待下去。超時(shí)會(huì)引發(fā)TimeoutException。但是這樣會(huì)導(dǎo)致你無(wú)法知道具體什么原因?qū)е翭uture無(wú)法返回,這時(shí)需要使用CompletableFUture的completeExceptionally方法將導(dǎo)致CompletableFuture內(nèi)發(fā)生的問(wèn)題拋出。
public Future<Double> getPriceAsync(String product){//創(chuàng)建CompletableFuture對(duì)象CompletableFuture<Double> futurePrice = new CompletableFuture<>();new Thread (()->{try {double price = getPrice(product);futurePrice.complete(price);} catch (Exception ex) {//拋出異常 futurePrice.completeExceptionally(ex);}}).start();return futurePrice;}調(diào)用時(shí):
System.out.println("begin");Future<Double> futurePrice = shop.getPriceAsync("ss");System.out.println("doSomething");try {System.out.println(futurePrice.get(1, TimeUnit.SECONDS));} catch (TimeoutException e) {System.out.print(e);}System.out.println("end");設(shè)置超時(shí)時(shí)間,然后會(huì)將錯(cuò)誤信息打印出來(lái)。
?
工廠方法supplyAsync創(chuàng)建CompletableFuture
使用工廠方法可以一句話來(lái)創(chuàng)建getPriceAsync方法
public Future<Double> getPriceAsync(String product) {return CompletableFuture.supplyAsync(() -> getPrice(product));}supplyAsync方法接受一個(gè)生產(chǎn)者(Supplier)作為參數(shù),返回一個(gè)CompletableFuture對(duì)象,該對(duì)象完成異步執(zhí)行后悔讀取調(diào)用生產(chǎn)者方法的返回值。生產(chǎn)者方法會(huì)交由ForkJoinPool池中的某個(gè)執(zhí)行線程(Executor)運(yùn)行,也可以調(diào)用supplyAsync方法的重載版本,傳入第二個(gè)參數(shù)指定不同的線程執(zhí)行生產(chǎn)者方法。 工廠方法返回的CompletableFuture對(duì)象也提供了同樣的錯(cuò)誤處理機(jī)制。
?
阻塞優(yōu)化
例如現(xiàn)在有一個(gè)商品列表,然后輸出一個(gè)字符串 商品名,價(jià)格 。
List<Shop> shops = Arrays.asList(new Shop("one"),new Shop("two"),new Shop("three"),new Shop("four"));long start = System.nanoTime();List<String> str = shops.stream().map(shop -> String.format("%s price: %.2f", shop.getName(), shop.getPrice(shop.getName()))).collect(toList());System.out.print(str);long end = System.nanoTime();System.out.print((end - start) / 1000000);[one price: 161.83, two price: 126.04, three price: 153.20, four price: 166.06]
4110
?
每次調(diào)用getPrice方法都會(huì)阻塞1秒鐘,對(duì)付這種我們可以使用并行流來(lái)進(jìn)行優(yōu)化:
List<String> str = shops.parallelStream().map(shop -> String.format("%s price: %.2f", shop.getName(), shop.getPrice(shop.getName()))).collect(toList());1137
?
明顯速度提升了,現(xiàn)在對(duì)四個(gè)商品查詢 實(shí)現(xiàn)了并行,所以只耗時(shí)1秒多點(diǎn),下面我們嘗試CompletableFuture:
List<CompletableFuture<String>> str2 = shops.stream().map(shop->CompletableFuture.supplyAsync(()->String.format("%s price: %.2f", shop.getName(), shop.getPrice(shop.getName())))).collect(toList());我們使用工廠方法supplyAsync創(chuàng)建CompletableFuture對(duì)象,使用這種方式我們會(huì)得到一個(gè)List<CompletableFuture<String>>,列表中的每一個(gè)ComplatableFuture對(duì)象在計(jì)算完成后都會(huì)包含商品的名稱。但是我們要求返回的是List<String>,所以需要等待所有的future執(zhí)行完畢,再將里面的值提取出來(lái),填充到列表中才能返回。
List<String> str3 =str2.stream().map(CompletableFuture::join).collect(toList());為了返回List<String> 需要對(duì)str2添加第二個(gè)map操作,對(duì)List中的所有future對(duì)象執(zhí)行join操作,一個(gè)接一個(gè)的等待他們的運(yùn)行結(jié)束。CompletableFuture類中的join和Future接口中的get方法有相同的含義,并且聲明在Future接口中,唯一的不同是join不會(huì)拋出任何檢測(cè)到的異常。
1149
現(xiàn)在使用了兩個(gè)不同的Stream流水線,而不是在同一個(gè)處理流的流水線上一個(gè)接一個(gè)的防治兩個(gè)map操作。考慮流操作之間的延遲特性,如果你在單一流水線中處理流,發(fā)向不同商家的請(qǐng)求只能以同步、順序執(zhí)行的方式才會(huì)成功。因此每個(gè)創(chuàng)建CompletableFuture對(duì)象只能在前一個(gè)操作結(jié)束之后,再join返回計(jì)算結(jié)果。
?
更好的解決方式
并行流的版本工作的非常好,那是因?yàn)樗梢圆⑿刑幚?個(gè)任務(wù),獲取操作系統(tǒng)線程數(shù)量:
System.out.print(Runtime.getRuntime().availableProcessors());但是如果列表是9個(gè)呢?那么執(zhí)行結(jié)果就會(huì)2秒。因?yàn)樗疃嘀荒茏?個(gè)線程處于繁忙狀態(tài)。 但是使用CompletableFuture允許你對(duì)執(zhí)行器Executor進(jìn)行配置,尤其是線程池的大小,這是并行流API無(wú)法實(shí)現(xiàn)的。
?
定制執(zhí)行器
//創(chuàng)建一個(gè)線程池,線程池的數(shù)目為100何商店數(shù)目二者中較小的一個(gè)值final Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100),new ThreadFactory() {@Overridepublic Thread newThread(Runnable r) {Thread t = new Thread(r);t.setDaemon(true); //使用守護(hù)線程 ---這種方式不會(huì)阻止程序的關(guān)停return t;}});這個(gè)線程池是一個(gè)由守護(hù)線程構(gòu)成的線程池,Java程序無(wú)法終止或退出正在運(yùn)行中的線程,所以最后剩下的那個(gè)線程會(huì)由于一直等待無(wú)法發(fā)生的事件而引發(fā)問(wèn)題。與此相反,如果將線程標(biāo)記為守護(hù)進(jìn)程,意味著程序退出時(shí)它也會(huì)被回收。這二者之間沒(méi)有性能上的差異?,F(xiàn)在可以將執(zhí)行器作為第二個(gè)參數(shù)傳遞給supplyAsync方法了。
CompletableFuture.supplyAsync(()->String.format("%s price: %.2f", shop.getName(), shop.getPrice(shop.getName())),executor)這時(shí),執(zhí)行9個(gè)商品時(shí),執(zhí)行速度只有1秒。 執(zhí)行18個(gè)商品時(shí)也是1秒。這種狀態(tài)會(huì)一直持續(xù),直到商店的數(shù)目達(dá)到我們之前計(jì)算的閥值。 處理需要大量使用異步操作的情況時(shí),這幾乎是最有效的策略。
?
對(duì)多個(gè)異步任務(wù)進(jìn)行流水線操作
我們?cè)谏唐分性黾右粋€(gè)枚舉Discount.Code 來(lái)代表每個(gè)商品對(duì)應(yīng)不同的折扣率,創(chuàng)建枚舉如下:
public class Discount {public enum Code{NONE(0),SILVER(5),GOLD(10),PLATINUM(15),DIAMOND(20);private final int value;Code(int value){this.value = value;}} }現(xiàn)在我們修改 getPrice方法的返回格式為:ShopName:price:DiscountCode 使用? : 進(jìn)行分割的返回值。
public String getPrice(String product){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}Double price = new Random().nextDouble() * product.charAt(0) + product.charAt(1);Discount.Code code = Discount.Code.values()[new Random().nextInt(Discount.Code.values().length)];return String.format("%s:%.2f:%s",name,price,code);}返回值:?one:120.10:GOLDD
將返回結(jié)果封裝到 Quote 類中:
public class Quote {private final String shopName;private final double price;private final Discount.Code discountCode;public Quote(String shopName, double price, Discount.Code code) {this.shopName = shopName;this.price = price;this.discountCode = code;}public static Quote parse(String s) {String[] split = s.split(":");String shopName = split[0];double price = Double.parseDouble(split[1]);Discount.Code discountCode = Discount.Code.valueOf(split[2]);return new Quote(shopName, price, discountCode);}public String getShopName() {return shopName;}public double getPrice() {return price;}public Discount.Code getDiscountCode() {return discountCode;} }parse方法 通過(guò)getPrice的方法 返回的字符串 會(huì)返回Quote對(duì)象,此外 Discount服務(wù)還提供了一個(gè)applyDiscount方法,它接收一個(gè)Quote對(duì)象,返回一個(gè)字符串,表示該Quote的shop中的折扣價(jià)格:
public class Discount {public enum Code{..}public static String applyDiscount(Quote quote){return quote.getShopName() + "price :" + Discount.apply(quote.getPrice() ,quote.getDiscountCode());}public static double apply(double price,Code code){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}return price * (100 - code.value) / 100;} }Discount中 也模擬了遠(yuǎn)程操作 睡了1秒鐘,首先我們嘗試最直接的方式:
List<String> str = shops.stream().map(shop->shop.getPrice("hhhhh")) //獲取 one:120.10:GOLDD 格式字符串.map(Quote::parse) //轉(zhuǎn)換為 Quote 對(duì)象.map(Discount::applyDiscount) //返回 Quote的shop中的折扣價(jià)格 .collect(toList());System.out.print(str);8146
首先,我們調(diào)用getPrice遠(yuǎn)程方法將shop對(duì)象轉(zhuǎn)換成了一個(gè)字符串。每個(gè)1秒
然后,我們將字符串轉(zhuǎn)換為Quote對(duì)象。
最后,我們將Quote對(duì)象 調(diào)用 遠(yuǎn)程 Discount服務(wù)獲取折扣,返回折扣價(jià)格。每個(gè)1秒
順序執(zhí)行4個(gè)商品是4秒,然后又調(diào)用了Discount服務(wù)又4秒 所以是8秒。 雖然我們現(xiàn)在把流轉(zhuǎn)換為并行流 性能會(huì)很好 但是數(shù)量大于8時(shí)也很慢。相反,使用自定義CompletableFuture執(zhí)行器能夠更充分的利用CPU資源。
List<CompletableFuture<String>> priceFutures = shops.stream()//異步獲取每個(gè)shop中的價(jià)格.map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice("hhhhh", executor)))//Quote對(duì)象存在時(shí),對(duì)其返回值進(jìn)行轉(zhuǎn)換.map(future -> future.thenApply(Quote::parse))//使用另一個(gè)異步任務(wù)構(gòu)造期望的future,申請(qǐng)折扣.map(future -> future.thenCompose(quote ->CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor))).collect(toList());//等待流中的所有Future執(zhí)行完畢,提取各自的返回值List<String> str = priceFutures.stream().map(CompletableFuture::join).collect(toList());System.out.print(str);2126
使用的這三個(gè)map跟同步?jīng)]有太大的區(qū)別,但是使用了CompletableFuture類提供的特性,在需要的地方把他們變成了異步操作。
thenApply方法:當(dāng)?shù)谝粋€(gè)Future運(yùn)行結(jié)束,返回CompletableFuture<String>對(duì)象轉(zhuǎn)換為CompleTableFuture<Quote>對(duì)象。
thenCompose方法:將兩個(gè)異步操作進(jìn)行流水線,當(dāng)?shù)谝粋€(gè)操作完成時(shí),將其結(jié)果作為參數(shù)傳遞給第二個(gè)操作。換句話說(shuō),你可以創(chuàng)建兩個(gè)CompletableFuture對(duì)象,對(duì)第一個(gè)對(duì)象調(diào)用thenCompose,并向其傳遞一個(gè)函數(shù)。
這個(gè)方法也有Async版本:thenComposeAsync,通常帶后綴的版本是講任務(wù)移交到一個(gè)新線程,不帶后綴的在當(dāng)前線程執(zhí)行。對(duì)于這個(gè)例子我們沒(méi)有加上后綴,因?yàn)閷?duì)于最終結(jié)果,或者大致的時(shí)間而言都沒(méi)有多少差別,少了很多線程切換的開(kāi)銷。
?
合并兩個(gè)CompletableFuture,無(wú)論是否依賴
與上面不同,第二個(gè)CompletableFuture無(wú)需等待第一個(gè)CompletableFuture運(yùn)行結(jié)束。而是,將兩個(gè)完全不相干的CompletableFuture對(duì)象整合起來(lái),不希望等到第一個(gè)任務(wù)完全結(jié)束才開(kāi)始第二個(gè)任務(wù)。
這種情況應(yīng)該使用thenCombine方法,它接受名為BiFunction的第二個(gè)參數(shù),這個(gè)參數(shù)定義了當(dāng)兩個(gè)CompletableFuture對(duì)象完成計(jì)算后,結(jié)果如何合并。同thenCompose方法一樣,thenCombine方法也提供了一個(gè)Async的版本。使用thenCombineAsync會(huì)導(dǎo)致BiFunction中定義的合并操作被提交到線程池中,由另一個(gè)任務(wù)以異步的方式執(zhí)行。
回到這個(gè)例子,比如說(shuō)我們現(xiàn)在需要第三個(gè)CompletableFuture來(lái)獲取匯率,展示美元。當(dāng)前兩個(gè)CompletableFuture計(jì)算出結(jié)果,并由BiFunction方法完全合并后,由它來(lái)最終將誒書(shū)這一任務(wù):
Future<Double> futurePriceUSD = CompletableFuture.supplyAsync(()->shops.get(0).getPrice("gg")).thenCombine(CompletableFuture.supplyAsync(()-> 0.66 //遠(yuǎn)程服務(wù)獲取 匯率),(price,rate) -> price * rate);這里 第一個(gè)參數(shù)price 是 getPrice的返回值 double , 第二個(gè)參數(shù) rate 是第二個(gè)工廠方法返回的0.66 偷了個(gè)懶, 最后是他們的結(jié)果進(jìn)行乘法操作 返回最終結(jié)果。
?
響應(yīng)CompletableFuture的completion事件
在本章中,所有的延遲例子都是延遲1秒鐘,但是在現(xiàn)實(shí)世界中,有時(shí)可能更糟。到目前為止,你所實(shí)現(xiàn)的方法必須等待所有的商品返回時(shí)才能現(xiàn)實(shí)商品的價(jià)格。而你希望的效果是,只要有商品返回商品價(jià)格就在第一時(shí)間顯示出來(lái),不用等待那些還沒(méi)有返回的商品。
CompletableFuture[] futures = shops.stream().map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice("hhhhh", executor))).map(future -> future.thenApply(Quote::parse)).map(future -> future.thenCompose(quote ->CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor)))//在每個(gè)CompletableFuture上注冊(cè)一個(gè)操作,該操作會(huì)在CompletableFuture完成后使用它的返回值。//使用thenAccept將結(jié)果輸出,它的參數(shù)就是 CompletableFuture的返回值。.map(f -> f.thenAccept(System.out::println))//你可以把構(gòu)成的Stream的所有CompletableFuture<void>對(duì)象放到一個(gè)數(shù)組中,等待所有的任務(wù)執(zhí)行完成.toArray(size -> new CompletableFuture[size]);//allOf方法接受一個(gè)CompletableFuture構(gòu)成的數(shù)組,數(shù)組中所有的COmpletableFuture對(duì)象執(zhí)行完成后,//它返回一個(gè)COmpletableFuture<Void>對(duì)象。所以你需要哦等待最初Stream中的所有CompletableFuture對(duì)象執(zhí)行完畢,//對(duì)allOf方法返回的CompletableFuture執(zhí)行join操作CompletableFuture.allOf(futures).join();Connected to the target VM, address: '127.0.0.1:62278', transport: 'socket'
8twoprice :113.31
threeprice :108.15
oneprice :137.844
Disconnected from the target VM, address: '127.0.0.1:62278', transport: 'socket'
fourprice :119.2725
3768
還有一個(gè)方法anyOf,對(duì)于CompletableFuture對(duì)象數(shù)組中有任何一個(gè)執(zhí)行完畢就不在等待時(shí)使用。
?
小結(jié):
1.執(zhí)行比較耗時(shí)的操作時(shí),尤其是那些依賴一個(gè)或多個(gè)遠(yuǎn)程服務(wù)的操作,使用異步任務(wù)可以改善程序的性能,加快程序的響應(yīng)速度。
2.你應(yīng)該盡可能的為客戶提供異步API。使用CompletableFuture類提供的特性,能夠輕松的實(shí)現(xiàn)這一目標(biāo)。
3.CompletableFuture類還提供了異常管理的機(jī)制,然給你有機(jī)會(huì)拋出/管理異步任務(wù)執(zhí)行中發(fā)生的異常。
4.將同步API的調(diào)用封裝到一個(gè)CompletableFuture中,你能夠以異步的方式使用其結(jié)果。
5.如果異步任務(wù)之間互相獨(dú)立,或者他們之間某一些的結(jié)果是另一些的輸入,你可以講這些異步任務(wù)合并成一個(gè)。
6.你可以為CompletableFuture注冊(cè)一個(gè)回調(diào)函數(shù),在Future執(zhí)行完畢或者他們計(jì)算的結(jié)果可用時(shí),針對(duì)性的執(zhí)行一些程序。
7.你可以決定在什么時(shí)候?qū)⒄O書(shū)程序的運(yùn)行,是等待由CompletableFuture對(duì)象構(gòu)成的列表中所有的對(duì)象都執(zhí)行完畢,還是只要其中任何一個(gè)首先完成就終止程序的運(yùn)行。
?
轉(zhuǎn)載于:https://www.cnblogs.com/baidawei/p/9447737.html
總結(jié)
以上是生活随笔為你收集整理的Java 8 (10) CompletableFuture:组合式异步编程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: idea git 发起一个pull r
- 下一篇: JavaSE基础知识(5)—面向对象(5