欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2021年11月04日 11:34:51   作者:小小工匠  
這篇文章主要介紹了Java8 使用CompletableFuture 構(gòu)建異步應(yīng)用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

概述

為了展示 CompletableFuture 的強大特性, 創(chuàng)建一個名為 best-price-finder 的應(yīng)用,它會查詢多個在線商店,依據(jù)給定的產(chǎn)品或服務(wù)找出最低的價格。

這個過程中,會學(xué)到幾個重要的技能。

  • 如何提供異步API
  • 如何讓你使用了同步API的代碼變?yōu)榉亲枞a

我們將共同學(xué)習(xí)如何使用流水線將兩個接續(xù)的異步操作合并為一個異步計算操作。 比如,在線商店返回了你想要購買的商品的原始價格,并附帶著一個折扣代碼——最終,要計算出該商品的實際價格,你不得不訪問第二個遠程折扣服務(wù),查詢該折扣代碼對應(yīng)的折扣比率

  • 如何以響應(yīng)式的方式處理異步操作的完成事件,以及隨著各個商品返回它的商品價格,最佳價格查詢器如何持續(xù)的更新每種商品的最佳推薦,而不是等待所有的商店都返回他們各自的價格(這種方式存在著一定的風(fēng)險,一旦某家商店的服務(wù)中斷,用戶可能遭遇白屏)。

在這里插入圖片描述

同步API VS 異步API

同步API

是對傳統(tǒng)方法的另一種稱呼:你調(diào)用了某個方法,調(diào)用方在被調(diào)用方運行的過程中會等待,被調(diào)用方運行結(jié)束返回,調(diào)用方取的了被調(diào)用方的返回值并繼續(xù)運行。

即使調(diào)用方和被調(diào)用方在不同的線程中運行,調(diào)用方還是需要等被調(diào)用方結(jié)束運行,這就是 阻塞式調(diào)用。

異步API

與同步API相反,異步API會直接返回,或者至少在被調(diào)用方計算完成之前,將它剩余的計算任務(wù)交給另一個線程去做,該線程和調(diào)用方是異步的。 這就是非阻塞調(diào)用。

執(zhí)行剩余的計算任務(wù)的線程將他的計算結(jié)果返回給調(diào)用方。 返回的方式要么通過回調(diào)函數(shù),要么由調(diào)用方再此執(zhí)行一個“等待,指導(dǎo)計算完成”的方法調(diào)用。

同步的困擾

為了實現(xiàn)最佳價格查詢器應(yīng)用,讓我們從每個商店都應(yīng)該提供的API定義入手。

首先,商店應(yīng)該聲明依據(jù)指定產(chǎn)品名稱返回價格的方法:

public class Shop {
	public double getPrice(String product) {
	// TODO
	}
}

該方法的內(nèi)部實現(xiàn)會查詢商店的數(shù)據(jù)庫,但也有可能執(zhí)行一些其他耗時的任務(wù),比如聯(lián)系其他外部服務(wù)。

用 delay 方法模擬這些長期運行的方法的執(zhí)行,模擬執(zhí)行1S ,方法聲明如下。

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

getPrice 方法會調(diào)用 delay 方法,并返回一個隨機計算的值

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

很明顯,這個API的使用者(這個例子中為最佳價格查詢器)調(diào)用該方法時,它依舊會被阻塞。為等待同步事件完成而等待1S,這是無法接受的,尤其是考慮到最佳價格查詢器對網(wǎng)絡(luò)中的所有商店都要重復(fù)這種操作。

接下來我們會了解如何以異步方式使用同步API解決這個問題。但是,出于學(xué)習(xí)如何設(shè)計異步API的考慮, 你希望以異步API的方式重寫這段代碼, 假裝我們還在深受這一困難的煩惱,如何以異步API的方式重寫這段代碼,讓用戶更流暢地訪問呢?


在這里插入圖片描述

實現(xiàn)異步API

將同步方法改為異步方法

為了實現(xiàn)這個目標,你首先需要將 getPrice 轉(zhuǎn)換為 getPriceAsync 方法,并修改它的返回值:

public Future<Double> getPriceAsync(String product) { ... }

我們知道 ,Java 5引入了 java.util.concurrent.Future 接口表示一個異步計算(即調(diào)用線程可以繼續(xù)運行,不會因為調(diào)用方法而阻塞)的結(jié)果 。

這意味著 Future 是一個暫時還不可知值的處理器,這個值在計算完成后,可以通過調(diào)用它的 get 方法取得。因為這樣的設(shè)計, getPriceAsync 方法才能立刻返回,給調(diào)用線程一個機會,能在同一時間去執(zhí)行其他有價值的計算任務(wù)。

新的 CompletableFuture 類提供了大量的方法,讓我們有機會以多種可能的方式輕松地實現(xiàn)這個方法,比如下面就是這樣一段實現(xiàn)代碼

【getPriceAsync方法的實現(xiàn)】

在這里插入圖片描述

在這段代碼中,創(chuàng)建了一個代表異步計算的 CompletableFuture 對象實例,它在計算完成時會包含計算的結(jié)果。

接著,調(diào)用 fork 創(chuàng)建了另一個線程去執(zhí)行實際的價格計算工作,不等該耗時計算任務(wù)結(jié)束,直接返回一個 Future 實例。

當(dāng)請求的產(chǎn)品價格最終計算得出時,你可以使用它的 complete 方法,結(jié)束completableFuture 對象的運行,并設(shè)置變量的值。

很顯然,這個新版 Future 的名稱也解釋了它所具有的特性。使用這個API的客戶端,可以通過下面的這段代碼對其進行調(diào)用。

【使用異步的API】

在這里插入圖片描述

我們看到這段代碼中,客戶向商店查詢了某種商品的價格。由于商?提供了異步API,該次調(diào)用立刻返回了一個 Future 對象,通過該對象客戶可以在將來的某個時刻取得商品的價格。

這種方式下,客戶在進行商品價格查詢的同時,還能執(zhí)行一些其他的任務(wù),比如查詢其他家商店中商品的價格,不會呆呆的阻塞在那里等待第一家商店返回請求的結(jié)果。

最后,如果所有有意義的工作都已經(jīng)完成,客戶所有要執(zhí)行的工作都依賴于商品價格時,再調(diào)用 Future 的 get 方法。執(zhí)行了這個操作后,客戶要么獲得 Future 中封裝的值(如果異步任務(wù)已經(jīng)完成),要么發(fā)生阻塞,直到該異步任務(wù)完成,期望的值能夠訪問。

輸出

在這里插入圖片描述

你一定已經(jīng)發(fā)現(xiàn) getPriceAsync 方法的調(diào)用返回遠遠早于最終價格計算完成的時間。

我們有可能避免發(fā)生客戶端被住阻塞的風(fēng)險。實際上這非常簡單, Future 執(zhí)行完畢可以發(fā)出一個通知,僅在計算結(jié)果可用時執(zhí)行一個由Lambda表達式或者方法引用定義的回
調(diào)函數(shù)。

不過,我們當(dāng)下不會對此進行討論,現(xiàn)在我們要解決的是另一個問題:如何正確地管理
異步任務(wù)執(zhí)行過程中可能出現(xiàn)的錯誤。

在這里插入圖片描述

處理異常錯誤

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

客戶端可以使用重載版本的 get 方法,它使用一個超時參數(shù)來避免發(fā)生這樣的情況。這是一種值得推薦的做法,你應(yīng)該盡量在你的代碼中添加超時判斷斷的邏輯,避免發(fā)生類似的問題。

使用這種方法至少能防止程序永遠的等待下去,超時發(fā)生時,程序會得到通知發(fā)生了 Timeout-Exception 。

不過,也因為如此,你不會有機會發(fā)現(xiàn)計算商品價格的線程內(nèi)到底發(fā)生了什么問題才引發(fā)了這樣的失效。

為了讓客戶端能了解商店無法提供請求商品價格的原因,你需要使用
CompletableFuture 的 completeExceptionally 方法將導(dǎo)致 CompletableFuture 內(nèi)發(fā)生問題的異常拋出。

代碼如下

【拋出CompletableFuture內(nèi)的異?!?/strong>

1

客戶端現(xiàn)在會收到一個 ExecutionException 異常,該異常接收了一個包含失敗原因的Exception 參數(shù),即價格計算方法最初拋出的異常。

所以,舉例來說,如果該方法拋出了一個運行時異?!皃roduct not available”,客戶端就會得到像下面這樣一段 ExecutionException :

java.util.concurrent.ExecutionException: java.lang.RuntimeException: product
not available at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2237)
at lambdasinaction.chap11.AsyncShopClient.main(AsyncShopClient.java:14)
... 5 more
Caused by: java.lang.RuntimeException: product not available
at lambdasinaction.chap11.AsyncShop.calculatePrice(AsyncShop.java:36)
at lambdasinaction.chap11.AsyncShop.lambda$getPrice$0(AsyncShop.java:23)
at lambdasinaction.chap11.AsyncShop$$Lambda$1/24071475.run(Unknown Source)
at java.lang.Thread.run(Thread.java:744)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java如何避免死鎖和競態(tài)條件的實現(xiàn)

    Java如何避免死鎖和競態(tài)條件的實現(xiàn)

    本文主要介紹了Java如何避免死鎖和競態(tài)條件的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • 詳解Java數(shù)據(jù)庫連接JDBC基礎(chǔ)知識(操作數(shù)據(jù)庫:增刪改查)

    詳解Java數(shù)據(jù)庫連接JDBC基礎(chǔ)知識(操作數(shù)據(jù)庫:增刪改查)

    這篇文章主要介紹了詳解Java數(shù)據(jù)庫連接JDBC基礎(chǔ)知識(操作數(shù)據(jù)庫:增刪改查),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-01-01
  • Java垃圾回收器的方法和原理總結(jié)

    Java垃圾回收器的方法和原理總結(jié)

    本篇文章主要介紹了Java垃圾回收器的方法和原理總結(jié),Java垃圾回收器是Java虛擬機的重要模塊,具有一定的參考價值,有興趣的可以了解一下。
    2016-12-12
  • Java IO流 File類的常用API實例

    Java IO流 File類的常用API實例

    這篇文章主要介紹了Java IO流 File類的常用API實例的相關(guān)資料,需要的朋友參考下吧
    2017-05-05
  • Java中的抽象類和接口你了解嗎

    Java中的抽象類和接口你了解嗎

    這篇文章主要為大家詳細介紹了Java中的抽象類和接口,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • Java自動拆箱空指針異常的解決

    Java自動拆箱空指針異常的解決

    這篇文章主要介紹了Java自動拆箱空指針異常的解決,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • 詳細解析Java中抽象類和接口的區(qū)別

    詳細解析Java中抽象類和接口的區(qū)別

    這篇文章主要介紹了Java中抽象類和接口的區(qū)別詳解,需要的朋友可以參考下
    2014-10-10
  • Java數(shù)據(jù)結(jié)構(gòu)優(yōu)先隊列實練

    Java數(shù)據(jù)結(jié)構(gòu)優(yōu)先隊列實練

    通常都把隊列比喻成排隊買東西,大家都很守秩序,先排隊的人就先買東西。但是優(yōu)先隊列有所不同,它不遵循先進先出的規(guī)則,而是根據(jù)隊列中元素的優(yōu)先權(quán),優(yōu)先權(quán)最大的先被取出,這篇文章主要介紹了java優(yōu)先隊列的真題,感興趣的朋友一起看看吧
    2022-07-07
  • ?java中StopWatch的使用詳解

    ?java中StopWatch的使用詳解

    本文主要介紹了?java中StopWatch的使用詳解,stopWatch使用它可直觀的輸出代碼執(zhí)行耗時,以及執(zhí)行時間百分比,非常具有實用價值,需要的朋友可以參考下
    2023-05-05
  • maven在IDEA下載依賴包速度慢的問題解決

    maven在IDEA下載依賴包速度慢的問題解決

    這篇文章主要介紹了maven在IDEA下載依賴包速度慢的問題解決方案,本文通過圖文結(jié)合的形式給大家分享解決方案,供大家參考,需要的朋友可以參考下
    2024-01-01

最新評論