java8——CompletableFuture

下圖表明了并發(fā)和并行的區(qū)別:


image

Future接口

Future接口在java5中被引入骗灶,設(shè)計初衷是對將來某個時刻會發(fā)生的結(jié)果進(jìn)行建模惨恭。它建模了一種一步計算。打個比方耙旦,你拿了一袋子衣服到你中意的干洗店去洗脱羡。干洗店的員工會給你張發(fā)票,告訴你什么時候你的衣服會洗好(這就是一個future事件)。衣服干洗的同事锉罐,你可以去做其他的事情帆竹。Future的另一個有點(diǎn)是它比更底層的Thread更易用,要使用Future脓规,通常你只需要將耗時的操作封裝在一個Callable對象中栽连,再將他交給ExecutorService,就萬事大吉了侨舆。

ExecutorService executor = Executors.newCachedThreadPool();
Future<Double> future = executor.submit(new Callable<Double>(){
    public Double call(){
        return doSomeLongComputation();
    }
});
doSomethingElse();
Double result = future.get(1,TimeUnit.SECONDS);

這種編程方式讓你的線程可以在ExecutorService以并發(fā)方式調(diào)用另一個線程執(zhí)行耗時操作的同時秒紧,去執(zhí)行一些其他的任務(wù)。接著挨下,如果你已經(jīng)運(yùn)行到?jīng)]有異步操作的結(jié)果就無法繼續(xù)任何有意義的工作時熔恢,可以調(diào)用他的get方法去獲取操作的結(jié)果。如果操作已經(jīng)完成臭笆,該方法就會立刻返回操作的結(jié)果叙淌,否則它會阻塞線程,直到操作完成愁铺,返回相應(yīng)的結(jié)果鹰霍。

如果長時間運(yùn)行的操作永遠(yuǎn)不反回了會怎樣?為了處理這種可能性帜讲,雖然Future提供了一個無需任何參數(shù)的get方法衅谷,還是推薦使用重載的版本椒拗,它接受一個超時的參數(shù)似将,通過它,可以定義線程等待Future結(jié)果的最長時間蚀苛。


image

Future接口的局限性

通過第一個例子在验,知道了Future接口提供了方法來檢測異步計算是否已經(jīng)結(jié)束(使用isDone()方法),等待異步操作結(jié)束堵未,以及獲取計算的結(jié)果腋舌。但是這些特性還不足以讓你編寫簡潔的并發(fā)代碼。比如渗蟹,我們很難表述Future結(jié)果之間的依賴性;從文字描述上很簡單块饺,“當(dāng)長時間計算任務(wù)完成時,請將該計算的結(jié)果通知到另一個長時間運(yùn)行的計算任務(wù)雌芽,這兩個計算任務(wù)都完成后時授艰,請將該計算的結(jié)果通知到另一個長時間運(yùn)行的計算任務(wù),這兩個計算任務(wù)都完成后,將計算的結(jié)果與另一個查詢操作結(jié)果合并”世落。但是淮腾,使用Future中提供的方法完成這樣的操作又是另一回事。這也是我們需要更具描述能力的特性的原因,比如下面這些:

  • 將兩個異步計算合并為一個——這兩個異步計算之間互相獨(dú)立谷朝,同時第二個又依賴于第一個的結(jié)果
  • 等待Future集合中的所有任務(wù)都完成
  • 僅等待Future集合中最快結(jié)束的任務(wù)完成(有可能因為它們師徒通過不同的方式計算同一個值)洲押,并返回它的結(jié)果。
  • 通過編程方式完成一個Future任務(wù)的執(zhí)行(即以手工設(shè)定異步操作結(jié)果的方式)
  • 應(yīng)對Future的完成事件(即當(dāng)Fureu的完成事件發(fā)生時會受到通知圆凰,并能使用Future計算的結(jié)果進(jìn)行下一步的操作杈帐,不止是簡單地紫色等待操作的結(jié)果)

使用CompletableFuture構(gòu)建異步應(yīng)用

為了展示CompletableFuture的強(qiáng)大特性,我們會創(chuàng)建一個名為“最佳加個查詢器”的應(yīng)用专钉,他會查詢多個在線商店娘荡,依據(jù)給定的產(chǎn)品或服務(wù)找出最低的價格。這個過程中驶沼,你會學(xué)到幾個重要的技能

  • 首先炮沐,你會學(xué)到如何為你的客戶提供異步API(如果你擁有一間在線商店的話,這是非常有幫助的)
  • 其次回怜,你會掌握如何讓你使用了同步API的代碼變?yōu)榉亲枞a大年。你會了解如何使用流水線將兩個連續(xù)的異步操作合并為一個異步計算操作。這種情況肯定會出現(xiàn)玉雾,比如翔试,在線商店返回了你想要購買商品的原始價格,并附帶著一個折扣代碼——最終复旬,要計算出該商品的實際價格垦缅,你不得不訪問第二個遠(yuǎn)程折扣服務(wù),長須改折扣代碼對應(yīng)的折扣比率驹碍。
  • 你還會學(xué)到如何以響應(yīng)式的方式處理異步操作的完成事件壁涎,以及隨著各個商店返回他的商品價格,最佳價格查詢器如何持續(xù)地更新每種商品的最佳推薦志秃,而不是等待所有的商店都返回他們各自的價格(這種方式存在著一定的風(fēng)險怔球,一旦某件商店的服務(wù)中斷,用戶可能遭受白屏)浮还。

實現(xiàn)異步API

為了實現(xiàn)最佳價格查詢器應(yīng)用,讓我們從每個商店都應(yīng)該提供的API定義入手竟坛。首先,商店應(yīng)該聲明依據(jù)指定產(chǎn)品名稱返回價格的方法:

public class Shop{
    public double getPrice(String produc){
        
    }
}

該方法的內(nèi)部實現(xiàn)會查詢商店的數(shù)據(jù)庫钧舌,但也有可能執(zhí)行一些其他耗時的任務(wù)担汤,比如聯(lián)系其他外部服務(wù)。所以會有耗時任務(wù)洼冻,我們在剩下的內(nèi)容中崭歧,采用delay方法模擬這些長期運(yùn)行的方法的執(zhí)行,它會人為地引入1秒鐘的延遲碘赖。

public static void delay(){
    try{
        Thread.sleep(1000L);
    }catch(InterruptedException e){
        thorw new RuntimeExcepiton(e);
    }
}

在getPrice方法中引入一個模擬的延遲

public double getPirce(String product){
    return calculatePrice(product);
}
private double calculatePrice(String product){
    delay();
    return random.nextDouble() * product.charAt(0) + product.charAt(1);
}

很明顯驾荣,這個API的使用者外构,調(diào)用該方法時,它依舊會被阻塞播掷。為等待同步事件完成而等待1秒鐘审编,這是無法接受的。

將同步方法轉(zhuǎn)換為異步方法

public Future<Double> getPriceAsync(String product){
    CompletableFuture<Double> futurePrice = new CompletableFuture<>();//創(chuàng)建Completablefuture對象歧匈,會包含計算結(jié)果
    new Thread(()->{
        double price = calculatePrice(product);//在另一個線程中以異步方式執(zhí)行計算
        fururePrice.complete(price);//需長時間計算的任務(wù)結(jié)束并得出結(jié)果時垒酬,設(shè)置future的返回值。
    }).start()
    return futurePrice;//無需等待還沒結(jié)束的計算件炉,直接返回Future對象勘究。
}

錯誤處理

如果沒有意外,我們目前開發(fā)的代碼工作的很正常斟冕。但是口糕,如果價格計算過程中產(chǎn)生了錯誤會怎么樣?非常不幸磕蛇,這種情況下你會得到一個相當(dāng)糟糕的結(jié)果:用于提示錯誤的異常會被限制在試圖計算商品價格的當(dāng)前線程的范圍內(nèi)景描,最終會殺死線程。而這會導(dǎo)致等待get方法返回結(jié)果的客戶端會被永久的阻塞秀撇。

public Future<Double> getPriceAsync(String product){
    CompletableFuture<Double> futurePrice = new CompletableFuture<>();//創(chuàng)建Completablefuture對象超棺,會包含計算結(jié)果
    new Thread(()->{
        try{
            double price = calculatePrice(product);//在另一個線程中以異步方式執(zhí)行計算
            fururePrice.complete(price);//需長時間計算的任務(wù)結(jié)束并得出結(jié)果時,設(shè)置future的返回值呵燕。
        }catch(Exception e){
            futurePrice.completeExceptionally(e); 
        }
    }).start()
    return futurePrice;//無需等待還沒結(jié)束的計算棠绘,直接返回Future對象。
}

使用工廠方法supplyAsync創(chuàng)建CompletableFuture
目前為止我們已經(jīng)了解了如何通過編程創(chuàng)建安CompletableFuture對象以及如何獲取返回值再扭,雖然看起來這些操作已經(jīng)比較方便氧苍,但還有進(jìn)一步的提升空間,CompletableFuture類自身提供了大量精巧的工廠方法霍衫,使用這些方法能更容易地萬恒整個流程候引。

使用工廠方法supplyAsync創(chuàng)建CompletableFure對象

public Future<Double> getPriceAsync(String product){
    return CompletableFuture.supplyAsync(() -> calculatePrice(product));
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市敦跌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逛揩,老刑警劉巖柠傍,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異辩稽,居然都是意外死亡惧笛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門逞泄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來患整,“玉大人拜效,你說我怎么就攤上這事「餮瑁” “怎么了紧憾?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長昌渤。 經(jīng)常有香客問我赴穗,道長,這世上最難降的妖魔是什么膀息? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任般眉,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘压鉴。我一直安慰自己闽颇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布辑奈。 她就那樣靜靜地躺著,像睡著了一般已烤。 火紅的嫁衣襯著肌膚如雪鸠窗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天胯究,我揣著相機(jī)與錄音稍计,去河邊找鬼。 笑死裕循,一個胖子當(dāng)著我的面吹牛臣嚣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播剥哑,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼硅则,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了株婴?” 一聲冷哼從身側(cè)響起怎虫,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎困介,沒想到半個月后大审,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡座哩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年徒扶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片根穷。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡姜骡,死狀恐怖导坟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情圈澈,我是刑警寧澤惫周,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站士败,受9級特大地震影響闯两,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜谅将,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一漾狼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧饥臂,春花似錦逊躁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至囚戚,卻和暖如春酵熙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背驰坊。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工匾二, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拳芙。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓察藐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親舟扎。 傳聞我的和親對象是個殘疾皇子分飞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)睹限,斷路器譬猫,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 標(biāo)簽:java 可完成的Future 回顧Future使用 CompletableFuture tip:**Asy...
    梓青閱讀 570評論 0 1
  • Java8 in action 沒有共享的可變數(shù)據(jù),將方法和函數(shù)即代碼傳遞給其他方法的能力就是我們平常所說的函數(shù)式...
    鐵牛很鐵閱讀 1,229評論 1 2
  • 這個夏天 南國北國的荷花都開好了 荷花 讓我想起…易安…… 常記溪亭日暮 沈醉不知?dú)w路 興盡晚回舟 誤入藕花深處 ...
    Aliceflorida閱讀 916評論 1 7
  • 歇伏后上班邦泄,早晨删窒,延續(xù)己有習(xí)慣先"外出轉(zhuǎn)一圈,負(fù)重爬上樓"顺囊。然后,吃早飯蕉拢,按時道別特碳。 在單位诚亚,五天是不變...
    良田666閱讀 157評論 0 0