1. 程式人生 > >java中的非同步處理和Feature介面(一)

java中的非同步處理和Feature介面(一)

原文地址:https://blog.csdn.net/wangdong5678999/article/details/81837387#_1

一、背景介紹

想象這樣一個場景:你可能希望為你的法國客戶提供指定主題的熱點報道。為實現這一功能,你需要向 谷歌或者Twitter的API請求所有語言中針對該主題最熱門的評論,可能還需要依據你的內部演算法 對它們的相關性進行排序。之後,你可能還需要使用谷歌的翻譯服務把它們翻譯成法語,甚至 利用谷歌地圖服務定位出評論作者的位置資訊,最終將所有這些資訊聚集起來,呈現在你的網站上。

在這種“混聚”應用式的應用中,我們的應用可能會有以下兩種需求:

由於我們呼叫的許多都是外部提供的介面,極有可能出現由於某些外部網路服務發生響應慢的情況。在這種情況下,我們可能希望依舊能為使用者提供部分資訊,比如提供帶問號標記的通用地圖,以文字的方式顯示資訊,而不是呆呆地顯示一片空白螢幕,直到地圖伺服器返回結果或者超時退出。

要實現類似的服務,你需要與網際網路上的多個Web服務通訊。可是,你並不希望因為等待某 些服務的響應,阻塞應用程式的執行,浪費數十億寶貴的CPU時鐘週期。比如,不要因為等待 Facebook的資料,暫停對來自Twitter的資料處理。

以上兩種場景體現了多工程式設計的另一面。如果你的主要目標是在同一個CPU上執 行幾個鬆耦合的任務,充分利用CPU的核,讓其足夠忙碌,從而最大化程式的吞吐量,那麼你其實真正想做的是避免因為等待遠端服務的返回,或者對資料庫的查詢,而阻塞執行緒的執行, 浪費寶貴的計算資源,因為這種等待的時間很可能相當長。這時就需要用到非同步處理,在Java 5中提供的Future介面和在Java 8 中的新版實現CompletableFuture,就是處理這種情況的利器。

二、Feature介面

Future介面在Java 5中被引入,設計初衷是對將來某個時刻會發生的結果進行建模。它建模 了一種非同步計算,返回一個執行運算結果的引用,當運算結束後,這個引用被返回給呼叫方。在 Future中觸發那些潛在耗時的操作把呼叫執行緒解放出來,讓它能繼續執行其他有價值的工作, 不再需要呆呆等待耗時的操作完成。

三、Feature介面和Tread的區別

Future的優點是它比 更底層的Thread更易用。要使用Future,通常你只需要將耗時的操作封裝在一個Callable對 象中,再將它提交給ExecutorService,就萬事大吉了。

四、Feature介面示例

下面是一個Feature的demo示例:

public void testFeature() {
	//建立Executor- Service,通 過它你可以 向執行緒池提 交任務
	ExecutorService executor = Executors.newCachedThreadPool();
	//向Executor- Service提交一個 Callable物件
	final Future<Double> futureRate = executor.submit(new Callable<Double>() {
		public Double call() {
			//以非同步方式在新的執行緒中執行耗時的操作
			return doSomeLongComputation();
		}
	});
	//非同步操作進行的同時你可以做其他的事情
	doSomethingElse();

	try {
		//獲取非同步操作的結果,如果最終被阻塞,無法得到結果,那麼在最多等待1秒鐘之後退出
		Double result = future.get(1, TimeUnit.SECONDS);
	} catch (ExecutionException e) {
		// 計算丟擲一個異常
		e.printStackTrace();
	} catch (InterruptedException ie) { // 當前執行緒在等待過程中被中斷
	} catch (TimeoutException te) { // 在Future物件完成之前超過已過期
	}
}

如上圖所示,這種程式設計方式讓你的執行緒可以在ExecutorService以併發方式調 用另一個執行緒執行耗時操作的同時,去執行一些其他的任務。接著,如果你已經執行到沒有非同步 操作的結果就無法繼續任何有意義的工作時,可以呼叫它的get方法去獲取操作的結果。如果操 作已經完成,該方法會立刻返回操作的結果,否則它會阻塞你的執行緒,直到操作完成,返回相應 的結果。如果該長時間執行的操作永遠不返回了會怎樣?為了處理這種可能性,雖然Future提供了一個無需任何引數的get方法,我們還是推薦大家使用重 載版本的get方法,它接受一個超時的引數,通過它,你可以定義你的執行緒等待Future結果的最 長時間,從而無需永無止境的等待下去。

五、Feature介面的侷限性

雖然Feature介面提供了方法來檢測非同步計算是否已經結束(使用 isDone方法),等待非同步操作結束,以及獲取計算的結果。但是這些特性還不足以讓你編寫簡潔的併發程式碼。

我們可能還需要更多的特性來幫助我們寫出更好非同步程式碼,如:

將兩個非同步計算合併為一個——這兩個非同步計算之間相互獨立,同時第二個又依賴於第 一個的結果。

等待Future集合中的所有任務都完成。

僅等待Future集合中最快結束的任務完成(有可能因為它們試圖通過不同的方式計算同一個值),並返回它的結果。

通過程式設計方式完成一個Future任務的執行(即以手工設定非同步操作結果的方式)。

應對Future的完成事件(即當Future的完成事件發生時會收到通知,並能使用Future

計算的結果進行下一步的操作,不只是簡單地阻塞等待操作的結果)。

下一節我們將介紹新的CompletableFuture類(它實現了Future介面)如何利用Java 8 的新特性以更直觀的方式將上述需求都變為可能。