本節(jié)內(nèi)容摘自《Java異步編程實(shí)戰(zhàn)》中的一小節(jié)匆绣。
前言
本篇主要講解如何使用JDK中的Future 實(shí)現(xiàn)異步編程,包括如何使用FutureTask 實(shí)現(xiàn)異步編程以及內(nèi)部實(shí)現(xiàn)原理以及FutureTask 的局限性崎淳。
JDK 中的Future
在Java并發(fā)包(JUC包)中Future 代表著異步計(jì)算的結(jié)果,F(xiàn)uture中提供了一些方法用來檢查計(jì)算結(jié)果的完成,還提供了同步等待任務(wù)執(zhí)行完成的方法森爽,以及獲取結(jié)果的方法。當(dāng)計(jì)算結(jié)果完成時爬迟,通過提供的get系列方法來獲取結(jié)果橘蜜,如果使用了不帶超時時間的方法來獲取結(jié)果付呕,則在計(jì)算結(jié)果完成前,調(diào)用線程會一直阻塞徽职,另外計(jì)算任務(wù)是可以通過cancel 方法來取消的象颖,前提就是任務(wù)未執(zhí)行完畢姆钉。
首先我們來看下Future 接口的結(jié)構(gòu)圖
V get() throws InterruptedException, ExecutionException;
等待異步計(jì)算任務(wù)完成并返回結(jié)果;如果任務(wù)未執(zhí)行完畢潮瓶,則一直阻塞等待任務(wù)的完成。
CancellationException:如果計(jì)算任務(wù)被取消 拋出次異常
ExecutionException:如果任務(wù)執(zhí)行中出現(xiàn)了異常
InterruptedException:如果等待結(jié)果線程被其他線程打斷
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
在異步計(jì)算任務(wù)未執(zhí)行完成時筋讨,等待 timeout 個 unit 時間后拋出TimeoutException 異常后返回,避免了調(diào)用線程死等悉罕,可以及時釋放的問題赤屋。
boolean isDone();
如果任務(wù)完成返回true壁袄,否則返回false ,注意這里的完成包括:任務(wù)正常完成、拋出異常而完成嗜逻、任務(wù)被取消
boolean cancel(boolean mayInterruptIfRunning);
嘗試取消任務(wù)的執(zhí)行涩僻,如果任務(wù)已經(jīng)完成或者已經(jīng)被取消栈顷,則取消失敗萄凤;如果任務(wù)還沒執(zhí)行則調(diào)用了該方法室抽,則任務(wù)永遠(yuǎn)不會被執(zhí)行靡努;如果任務(wù)已經(jīng)開始運(yùn)行晓折,這時候取消任務(wù),則參數(shù)mayInterruptIfRunning決定是否要要將正在執(zhí)行的任務(wù)中斷漓概;
boolean isCancelled();
如果任務(wù)在執(zhí)行完畢前被取消了,則返回true病梢,否則返回 false。
JDK中的FutureTask
FutureTask 代表了一個可被取消的異步計(jì)算任務(wù)飘千,該類實(shí)現(xiàn)了RunnableFuture接口堂鲜,既包含Runnable 功能护奈,也包含F(xiàn)uture 功能。提供了啟動和取消任務(wù)霉旗,查詢?nèi)蝿?wù)是否完成痴奏,獲取計(jì)算結(jié)果等厌秒。
FutureTask 任務(wù)的執(zhí)行結(jié)果只有當(dāng)任務(wù)完成以后才能獲取。并且只有通過get系列方法獲取鸵闪。當(dāng)計(jì)算未完成時,get方法會阻塞等待結(jié)果蚌讼,任務(wù)一旦被執(zhí)行完成辟灰,除非運(yùn)行的時候用了runAndReset 方法篡石,否則任務(wù)不能被重啟。FutureTask 中的任務(wù)可以是Callable 類型的凰萨,也可以是Runnable 類型的继控,F(xiàn)utureTask 類型的任務(wù)可以被提交到線程池胖眷。
FutureTask 實(shí)現(xiàn)了Future 接口的所有方法,并且實(shí)現(xiàn)了Runnable 珊搀,所以其是可執(zhí)行任務(wù),可以投遞到線程池或者由線程來執(zhí)行食棕。
FutureTask 中變量state 是一個使用volatile 關(guān)鍵字修飾的(用來解決內(nèi)存可見性防止指令重排)int變量朗和。用來記錄任務(wù)的狀態(tài)簿晓。
FutureTask的局限性
FutureTask 雖然提供了用來檢查任務(wù)是否完成,等待任務(wù)執(zhí)行結(jié)果憔儿,獲取任務(wù)執(zhí)行結(jié)果的方法忆植,但是這些特色并不足以讓我們寫出簡潔的并發(fā)代碼谒臼,比如它并不清楚的表達(dá)出多個FutureTask 之間的關(guān)系,另外為了從Future獲取結(jié)果蜈缤,我們必須調(diào)用get()方法拾氓,而該方法還是會在任務(wù)執(zhí)行完畢前阻塞調(diào)用線程底哥,這明顯不是我們想要的。
我們真正想要的是:
1趾徽、可以將兩個或多個異步計(jì)算結(jié)合在一起變成一個续滋,這包括兩個或多個異步計(jì)算是獨(dú)立的時候孵奶,或者第二個異步計(jì)算依賴于第一個異步計(jì)算。
2了袁、對反應(yīng)式編程的支持朗恳,也就是當(dāng)任務(wù)計(jì)算完成后進(jìn)行通知早像,并且可以將計(jì)算結(jié)果作為下一步計(jì)算動作的參數(shù),而不是僅僅提供調(diào)用線程以阻塞的方式獲取結(jié)果卢鹦。
3臀脏、可以通過編程的方式手動設(shè)置Future 的結(jié)果冀自,F(xiàn)utureTask 則不可用讓用戶通過函數(shù)設(shè)置其計(jì)算結(jié)果,而是其任務(wù)內(nèi)部進(jìn)行設(shè)置熬粗。
4搀玖、可以等多個Future 對應(yīng)的計(jì)算結(jié)果都出來后做一些事情驻呐。
為了克服FutureTask的局限性芳来,以及滿足我們對異步編程的需要,JDK8中提供了CompletableFuture猜拾,CompletableFuture是一個可以通過編程方式顯式的設(shè)置計(jì)算結(jié)果和狀態(tài)以便讓任務(wù)結(jié)束的Future,本書后面章節(jié)我們會具體講解。
總結(jié)
推薦書目:《Java異步編程實(shí)戰(zhàn)》