日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【JUC系列】Future异步回调模式

發布時間:2024/10/5 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【JUC系列】Future异步回调模式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

何為異步回調

前面只是一個例子,對并發的主要模式進行形象的說明。

下面正式來講下經常使用的幾個和并發相關的概念。

1.2.1. 同步、異步、阻塞、非阻塞

一:同步

所謂同步,就是在發出一個功能調用時,在沒有獲得結果以前,該調用就不返回。也就是必須一件一件事作,等前一件作完了才能作下一件事。

單線程模式,就是絕對同步的。

二: 異步

異步首先必須是多線程模式。是指當前線程,向其余的異步線程發出調用指令。當前線程和異步線程,邏輯上同時執行。

三:阻塞

在異步的場景下,當前線程阻塞住,等待異步線程的執行結果。阻塞是指線程進入非可執行狀態,在這個狀態下,cpu不會給線程分配時間片,即線程暫停運行。

阻塞模式是效率比較低的,若是阻塞嚴重的話,至關于又回到了同步的時代。

四:非阻塞

非阻塞和阻塞的概念相對應,指在不能馬上獲得結果以前,當前線程不會阻塞住,而會繼續向下執行。回調就是一種非阻塞的異步模式。并發線程經過回調,能夠將結果返回給發起線程。除了回調,還有其余的非阻塞異步模式,好比消息通信、信號量等等。

1.2.2. 阻塞模式的泡茶案例圖解

阻塞模式的泡茶模型,對應到前面的第二種泡茶喝的工序模型。

在阻塞模式泡茶喝的模型中,有三條線程,他們分別是:

線程一:燒水線程

洗好水壺,灌上涼水,放在火上;

線程二:清洗線程

洗茶壺、洗茶杯;

線程三:主線程

分別啟動燒水線程、清洗線程。等水開了,等水杯洗好了,然后泡茶喝。

具體如下圖:

1.2.3. 回調模式的泡茶方法

前面提到,阻塞模式的效率不是最高的。

更高效率的是回調模式。主線程在等待的時間了,不是死等,而是去干讀書的活兒。等其他兩條線程完成后,通過回調方式,去完成泡茶的動作。

在回調模式泡茶喝的模型中,還是三條線程,他們的工作稍微有些變動:

線程一:燒水線程

洗好水壺,灌上涼水,放在火上;燒好水后,去執行泡茶回調。

線程二:清洗線程

洗茶壺、洗茶杯;清洗完成后,也去執行一下泡茶的動作。

線程三:主線程

分別啟動燒水線程、清洗線程。然后去讀書。

具體如下圖:

嚴格來說,上圖是經不起推敲的。

為啥呢? 那個泡茶喝回調方法,在執行的流程上,不屬于主線程在執行。只是在業務邏輯上,泡茶喝這個動作與主線程上的其他動作,關聯性更強。

上圖,更好的理解方式是,盡量站在業務流程的角度去理解。

回調不是唯一的非阻塞方式。

還有線程間通信、信號量等等,很多的非阻塞方式。但是回調卻是一種最好用的、也是開發中用的最多的線程間非阻塞的交互方式。

下面,從最原始的阻塞模式講起,起底整個異步回調模式。

?

1.3.?異步阻塞悶葫蘆——join

Java中,線程有一個join操作,也叫線程的合并。

join操作的作用,就是完成異步阻塞的工作——阻塞當前的線程,直到異步的并發線程的執行完成。

1.3.1. 線程的join 合并

如果線程A的執行過程中,通過B.join操作,合并B線程,叫做線程的合并。合并的重要特點之一是,線程A進入阻塞模式,直到B線程執行完成。

為了方便表達,模擬一下包工頭的甲方和乙方。

將發起合并的線程A叫做甲方線程,被發起的線程B為乙方線程。

簡單的說,線程合并就是——甲方等待乙方執行完成。換句話說,甲方將乙方線程合并到甲方線程。

在泡茶喝的例子中,主線程通過join操作,等待燒水線程和清洗線程。這就是一種異步阻塞。

1.3.2. join 異步阻塞實例代碼

先看實例,再看方法的詳細介紹。

泡茶喝的異步阻塞版本,實現如下:

package com.crazymakercircle.coccurent; import com.crazymakercircle.util.Print; /*** Created by 尼恩 at 瘋狂創客圈*/ public class JoinDemo {public static final int SLEEP_GAP = 500;public static String getCurThreadName() {return Thread.currentThread().getName();}static class HotWarterThread extends Thread {public HotWarterThread() {super("** 燒水-Thread");}public void run() {try {Print.tcfo("洗好水壺");Print.tcfo("灌上涼水");Print.tcfo("放在火上");//線程睡眠一段時間,代表燒水中Thread.sleep(SLEEP_GAP);Print.tcfo("水開了");} catch (InterruptedException e) {Print.tcfo(" 發生異常被中斷.");}Print.tcfo(" 運行結束.");}}static class WashThread extends Thread {public WashThread() {super("$$ 清洗-Thread");}public void run() {try {Print.tcfo("洗茶壺");Print.tcfo("洗茶杯");Print.tcfo("拿茶葉");//線程睡眠一段時間,代表清洗中Thread.sleep(SLEEP_GAP);Print.tcfo("洗完了");} catch (InterruptedException e) {Print.tcfo(" 發生異常被中斷.");}Print.tcfo(" 運行結束.");}}public static void main(String args[]) {Thread hThread = new HotWarterThread();Thread wThread = new WashThread();hThread.start();wThread.start();try {// 合并燒水-線程hThread.join();// 合并清洗-線程wThread.join();Thread.currentThread().setName("主線程");Print.tcfo("泡茶喝");} catch (InterruptedException e) {Print.tcfo(getCurThreadName() + "發生異常被中斷.");}Print.tcfo(getCurThreadName() + " 運行結束.");} }

演示程序中有三條線程:

一條是主線程main;

一條是燒水線程“hThread”;

一條是清洗線程“wThread”;

main線程,調用了hThread.join()實例方法,合并燒水線程,也調用了 wThread.join()實例方法,合并清洗線程。

另外說明一下:hThread是這里的燒水線程實例的句柄,"** 燒水-Thread"是燒水線程實例的線程名稱,兩者不能混淆。

1.3.3. join方法的詳細介紹

join的方法應用場景:異步阻塞場景。

具體來說:甲方(發起線程)的調用乙方(被發起線程)的join方法,等待乙方執行完成;如果乙方沒有完成,甲方阻塞。

join是Thread類的一個實例方法,使用的方式大致如下:

// 合并燒水-線程 hThread.join(); // 合并清洗-線程 wThread.join();

實際上,join方法是有三個重載版本:

實際上,join方法是有三個重載版本:

(1)void join(): 等待乙方線程執行結束,甲方線程重啟執行。

(2)void join(long millis): 等待乙方線程執行一段時間,最長等待時間為 millis 毫秒。超過millis 毫秒后,不論乙方是否結束,甲方線程重啟執行。

(3)void join(long millis, int nanos): 等待乙方線程執行一段時間,最長等待時間為 millis 毫秒,加nanos 納秒。超過時間后,不論乙方是否結束,甲方線程重啟執行。

強調一下容易混淆的幾點:

(1)join方法是實例方法,需要使用線程句柄去調用,如thread.join();

(2)執行到join代碼的時候,不是thread所指向的線程阻塞,而是當前線程阻塞;

(3)thread線程代表的是被合并線程(乙方),當前線程阻塞線程(甲方)。當前線程讓出CPU,進入等待狀態。

(4)只有等到thread線程執行完成,或者超時,當前線程才能啟動執行。

join合并有一個很大的問題,就是沒有返回值。

如果燒水線程的水有問題,或者燒水壺壞了,mian線程是沒有辦法知道的。

如果清洗線程的茶杯有問題,清洗不來了,mian線程是沒有辦法知道的。

形象的說,join線程就是一個悶葫蘆。

還是異步阻塞,但是需要獲得結果,怎么辦呢?

可以使用java 的FutureTask 系列類。

1.4.?異步阻塞重武器——FutureTask系列類

FutureTask相關的類型,處于java.util.concurrent包中,不止一個類,是一個系列。同時,這也是Java語言在1.5 版本之后提供了一種的新的多線程使用方法。

1.4.1. Callable接口

我們知道,異步線程的一個重要接口是Runnable,這里執行異步線程的業務代碼。但是,Runnable的run方法有一個問題,它是沒有返回的。

因此,Runnable不能用在需要有異步返回值的異步場景。

Java語言在1.5 版本之后重新定義了一個新的、類似Runnable的接口,Callable接口,將run方法改為了call方法,并且帶上了返回值。

Callable的代碼如下:

package java.util.concurrent; @FunctionalInterface public interface Callable<V> {V call() throws Exception; }

Callable接口位于java.util.concurrent包中,Callable接口是一個泛型接口。也是一個“函數式接口”。唯一的抽象方法call有返回值,返回值類型為泛型形參類型。call抽象方法還有一個Exception的異常聲明,容許方法的實現版本內部的異常不經過捕獲。

Callable接口類似于Runnable。不同的是,Runnable的唯一抽象方法run沒有返回值,也沒有強制審查異常的異常聲明。比較而言,Callable接口的功能更強大一些。

有一個異想天開的問題

作為新版的Callable接口實例,能否作為Thread線程實例的target來使用呢?

答案是不能。

Callable接口與Runnable接口之間沒有任何的繼承關系,而且二者唯一方法在的名字上也不同。Callable接口實例沒有辦法作為Thread線程實例的target來使用。

我們知道,java里邊的線程類型,就是Thread。Callable需要異步執行,就需要和Thread建立聯系。java提供了一個搭橋的角色——FutureTask類。

1.4.2. FutureTask類初探

顧名思義,這個是一個未來執行的任務,就相當于新線程所執行的操作。

FutureTask 類也位于 java.util.concurrent包。

FutureTask類 構造函數的參數為 Callable,并且間接的繼承了Runnable接口。其構造器代碼如下:

public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable }

到了這里,FutureTask類的作用就大致明白了。

如果還不明白,看一段實例代碼:

Callable<Boolean> hJob = new HotWarterJob(); FutureTask<Boolean> hTask = new FutureTask<Boolean>(hJob); Thread hThread = new Thread(hTask, "** 燒水-Thread");

FutureTask就像一座位于Callable與Thread之間的橋。FutureTask 封裝一個Callable,然后自身又作為Thread線程的target。

FutureTask還有一個十分重要的貢獻。

Thread線程執行過程中,異步線程的代碼邏輯在Callable的call方法中,而call方法返回的結果,則需要通過 FutureTask 去獲取。

好了,這下就應該基本清楚了。

總結一下FutureTask這個媒婆的作用:

(1)負責牽線

(2)通過媒婆取得結果

為了完成這個兩個偉大的使命,FutureTask有個相對比較復雜的繼承關系,具體如下圖:

首先,FutureTask實現了一個接口——RunnableFuture接口,而該RunnableFuture接口繼承了Runnable接口和Future接口。

Runnable接口我們很熟悉,就是那個java 線程Runnable,代表異步線程的代碼邏輯。

Future接口又是啥呢?

提前劇透下,這個接口,就是用來獲取異步線程結果的。

Future接口和Runnable接口一樣,都是牛氣沖天的接口。 而FutureTask 間接的實現這個兩大接口。

正因為FutureTask能夠有兩個很牛逼的爹,所以自己家才很牛逼。

FutureTask 既能當做一個Runnable 作為 target ,直接被Thread執行;也能作為Future用來去取得Callable的計算結果。

1.4.3. Future接口

Future接口這個不是一個復雜的接口,梳理一下,主要提供了3大功能:

(1)獲取并發的任務完成后的執行結果。

(2)能夠取消并發執行中的任務;

(3)判斷并發任務是否執行完成;

當然,第一點是最為常用的。也是這個接口的最初使命。

Future接口的代碼如下:

package java.util.concurrent; public interface Future<V> { ? boolean cancel(boolean mayInterruptRunning); ? boolean isCancelled(); ? boolean isDone(); ? V get() throws InterruptedException, ExecutionException; ? V get(long timeout,TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }

對Future接口的方法,詳細說明如下:

V get() :獲取并發任務執行的結果。注意,這個方法是阻塞性的。如果并發任務沒有執行完成,調用此方法的線程會一直阻塞,直到并發任務執行完成。

V get(Long timeout , TimeUnit unit) :獲取并發任務執行的結果。也是阻塞性的,但是會有阻塞的時間限制,如果阻塞時間超過設定的timeout時間,該方法將拋出異常。

boolean isDone():獲取并發任務的執行狀態。如果任務執行結束,返回true。

boolean isCancelled():獲取并發任務的取消狀態。如果任務完成前被取消,則返回true。

boolean cancel(boolean mayInterruptRunning):取消并發任務的執行。

1.4.4. FutureTask再次深入

說完了FutureTask的兩個爹,再來到FutureTask自身。

在FutureTask內部,又有哪些成員和方法,具體的執行并發任務、異步獲取任務結果的呢?

首先,FutureTask內部有一個 Callable類型的成員:

private Callable callable;

這個callable實例屬性,是構造器傳進來的。用來保存并發執行的 Callable類型的任務。callable實例屬性,是構造器強制性的,必須要在FutureTask實例構造的時候進行初始化。

其次,FutureTask內部有一個run方法。

這個run方法,是Runnable接口在FutureTask內部的實現。在這個run方法其中,會執行到callable成員的call方法。執行完成后,結果如何提供出去呢?這就是到了最后一點。

最后,FutureTask內部有另一個 Object 類型的重要成員——outcome實例屬性:

private Object outcome;

掐指一算,就知道這個outcome屬性,是用來保存callable成員call方法的執行結果。FutureTask類run方法執行完成callable成員的call方法后,會將結果保存在outcome實例屬性,供FutureTask類的get實例方法獲取。

好了,重要將這個媒婆介紹完了。

如果還沒有清楚,不要緊,看一個實例就一目了然了。

1.4.5. 喝茶實例演進之——獲取異步結果

回顧一下,前面的join悶葫蘆合并阻塞有一個很大的問題,就是沒有返回值。

如果燒水線程的水有問題,或者燒水壺壞了,mian線程是沒有辦法知道的。

如果清洗線程的茶杯有問題,清洗不來了,mian線程是沒有辦法知道的。

為了演示結果,給主類增加兩個成員:

static boolean warterOk = false; static boolean cupOk =false;

代表燒水成功和清洗成功。初始值都為false。

燒水線程、清洗線程執行完后,都需要返回結果。 主線程獲取后,保存在上面的兩個主類成員中。

廢話不多說,看代碼:

package com.crazymakercircle.coccurent; import com.crazymakercircle.util.Print; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /*** Created by 尼恩 at 瘋狂創客圈*/ public class JavaFutureDemo {public static final int SLEEP_GAP = 500;public static String getCurThreadName(){return Thread.currentThread().getName();}static class HotWarterJob implements Callable<Boolean> //①{@Overridepublic Boolean call() throws Exception //②{try{Print.tcfo("洗好水壺");Print.tcfo("灌上涼水");Print.tcfo("放在火上");//線程睡眠一段時間,代表燒水中Thread.sleep(SLEEP_GAP);Print.tcfo("水開了");} catch (InterruptedException e){Print.tcfo(" 發生異常被中斷.");return false;}Print.tcfo(" 運行結束.");return true;}}static class WashJob implements Callable<Boolean>{@Overridepublic Boolean call() throws Exception{try{Print.tcfo("洗茶壺");Print.tcfo("洗茶杯");Print.tcfo("拿茶葉");//線程睡眠一段時間,代表清洗中Thread.sleep(SLEEP_GAP);Print.tcfo("洗完了");} catch (InterruptedException e){Print.tcfo(" 清洗工作 發生異常被中斷.");return false;}Print.tcfo(" 清洗工作 運行結束.");return true;}}static boolean warterOk = false;static boolean cupOk =false;public static void drinkTea(){if (warterOk && cupOk){Print.tcfo("泡茶喝");}else if (!warterOk){Print.tcfo("燒水失敗,沒有茶喝了");}else if (!cupOk){Print.tcfo("杯子洗不了,沒有茶喝了");}}public static void main(String args[]){Callable<Boolean> hJob = new HotWarterJob();//③FutureTask<Boolean> hTask =new FutureTask<Boolean>(hJob);//④Thread hThread = new Thread(hTask, "** 燒水-Thread");//⑤Callable<Boolean> wJob = new WashJob();//③FutureTask<Boolean> wTask =new FutureTask<Boolean>(wJob);//④Thread wThread = new Thread(wTask, "$$ 清洗-Thread");//⑤hThread.start();wThread.start();Thread.currentThread().setName("主線程");try{warterOk = hTask.get();cupOk = wTask.get(); // hThread.join(); // wThread.join();drinkTea();} catch (InterruptedException e){Print.tcfo(getCurThreadName() + "發生異常被中斷.");} catch (ExecutionException e){e.printStackTrace();}Print.tcfo(getCurThreadName() + " 運行結束.");} }

1.4.6. FutureTask使用流程

借助上面的喝茶實例代碼,說明一下通過FutureTask獲取異步結果的流程步驟:

(1)異步代碼邏輯需要繼承Callable,通過call方法返回具體的值

static class WashJob implements Callable<Boolean> {@Overridepublic Boolean call() throws Exception{ //..業務代碼,并且有返回值 }

(3)從異步邏輯到異步線程,需要媒婆類FutureTask搭橋

Callable<Boolean> hJob = new HotWarterJob();//異步邏輯 FutureTask<Boolean> hTask = new FutureTask<Boolean>(hJob);//媒婆實例 Thread hThread = new Thread(hTask, "** 燒水-Thread");//異步線程

FutureTask和Callable都是泛型類,泛型參數表示返回結果的類型。所以,在使用的時候,倆個類型的泛型參數一定需要一致的。

FutureTask和Callable都是泛型類,泛型參數表示返回結果的類型。所以,在使用的時候,倆個類型的泛型參數一定需要一致的。

(3)取得異步線程的執行結果,也需要FutureTask 媒婆實例做下二傳

warterOk = hTask.get();

1.5.?Guava 的異步回調

在非常著名的google 提供的擴展包 Guava中,提供了一種異步回調的解決方案。

為了實現異步回調,Guava 對Java的Future 異步模式進行能力導入:

(1)導入了一個新的接口 FutureCallback,代表回調執行的業務邏輯

(2)對Java并發包中的 Future 接口進行了擴展,將回調邏輯作為監聽器綁定到異步線程

1.5.1. 能力導入 —— FutureCallback

FutureCallback 是一個新增的接口,用來填寫回調邏輯。這個接口,是在實際開發中編程使用到的。回調的代碼,編寫在它的實現類中。

FutureCallback擁有兩個回調方法:

(1)onSuccess ,在異步線程執行成功回調

(2)onFailure,在異步線程拋出異常時回調

FutureCallback的源碼如下:

public interface FutureCallback<V> {void onSuccess(@Nullable V var1);void onFailure(Throwable var1); }

1.5.2. 能力擴展 —— ListenableFuture

如果將回調方法,綁定到異步線程去呢?

Guava中,有一個非常關鍵的角色,ListenableFuture。看名稱,就能對應出它與Java 中的原生接口的親戚關系。

如果沒有猜錯,這個接口是 Guava 對java 的Future接口的擴展。

來看看 ListenableFuture接口的源碼,如下:

package com.google.common.util.concurrent; import java.util.concurrent.Executor; import java.util.concurrent.Future; public interface ListenableFuture<V> extends Future<V> {void addListener(Runnable var1, Executor var2); }

前面講到,通過Java的Future接口,可以阻塞取得異步的結果。在這個基礎上,ListenableFuture增加了一個方法 —— addListener 。

這個方法的作用,就是將前一小節的FutureCallback 回調邏輯,綁定到異步線程上。 可以是,addListener 不直接在實際編程中使用。這個方法只在Guava內部使用,如果對它感興趣,可以查看Guava源碼。

既然addListener 方法不能直接使用,那么,在實際編程中,如何將 FutureCallback 回調邏輯綁定到異步線程呢?

不慌,辦法總是有的。

需要用到Guava的Futures 工具類。這個類有一個addCallback 靜態方法,將ListenableFuture 的實例和FutureCallback 的回調實例,進行綁定。

綁定的示意代碼如下:

Futures.addCallback( hFuture , new FutureCallback<Boolean>() {public void onSuccess(Boolean r){ //成功時候的回調邏輯}public void onFailure(Throwable t){//異常時候的回調邏輯 ? } });

1.5.3. ListenableFuture 實例從何而來

從上文已知,原生java的Future接口的實例,一種方法是——直接構建媒婆類FutureTask的實例,就是Future接口的實例。

當然,還有第二種方法,就是通過線程池獲取Future接口的實例。具體的做法是向Java線程池提交異步任務,包括Runnable或者Callable實例。

方法如下:

Future<Boolean> hTask = pool.submit(hJob); Future<Boolean> wTask = pool.submit(wJob);

注意,pool 是一個Java 線程池。

如果要獲取Guava的ListenableFuture 實例,主要是通過類似上面的第二種方式——向線程池提交任務的異步任務的方式獲取。不過,用到的線程池,是Guava的線程池,不是Java的線程池。

Guava線程池,而是對Java線程池的一種裝飾。

兩種線程池的創建代碼,具體如下:

//java 線程池 ExecutorService jPool =Executors.*newFixedThreadPool*(10);//guava 線程池 ListeningExecutorService gPool =MoreExecutors.*listeningDecorator*(jPool);

有了Guava的線程池之后,就可以通過提交任務,來獲取ListenableFuture 實例了。代碼如下 :

ListenableFuture<Boolean> hFuture = gPool.submit(hJob);

關于Gava的線程池,請關注【瘋狂創客圈】的線程池的博客文章。

1.5.4. Guava異步回調的流程

總結一下,Guava異步回調的流程如下:

第一步:創建Java的 Callable的異步任務實例。實例如下:

Callable<Boolean> hJob = new HotWarterJob();//異步任務Callable<Boolean> wJob = new WashJob();//異步任務

異步任務也可以是Runnable類型。

第二步: 獲取Guava線程池

//java 線程池 ExecutorService jPool =Executors.*newFixedThreadPool*(10); //guava 線程池 ListeningExecutorService gPool =MoreExecutors.*listeningDecorator*(jPool);

?第三步: 提交異步任務到Guava線程池,獲取ListenableFuture 實例

ListenableFuture<Boolean> hFuture = gPool.submit(hJob);

第四步:創建回調的 FutureCallback 實例,通過Futures.addCallback,將回調邏輯綁定到ListenableFuture 實例。

Futures.*addCallback*( hFuture , new FutureCallback<Boolean>() {public void onSuccess(Boolean r){ //成功時候的回調邏輯}public void onFailure(Throwable t){//異常時候的回調邏輯 ? } });

?完成以上四步,當異步邏輯執行完成后,就會回調FutureCallback 實例中的回調代碼。

1.5.5. 喝茶實例 —— 異步回調演進

已經對喝茶實例的代碼非常熟悉下,下面是Guava的異步回調的演進版本,代碼如下:

package com.crazymakercircle.coccurent; import com.crazymakercircle.util.Print; import com.google.common.util.concurrent.*; import java.util.concurrent.*; /*** Created by 尼恩 at 瘋狂創客圈*/ public class GuavaFutureDemo {public static final int SLEEP_GAP = 500;public static String getCurThreadName(){return Thread.currentThread().getName();}static class HotWarterJob implements Callable<Boolean> //①{@Overridepublic Boolean call() throws Exception //②{try{Print.tcfo("洗好水壺");Print.tcfo("灌上涼水");Print.tcfo("放在火上");//線程睡眠一段時間,代表燒水中Thread.sleep(SLEEP_GAP);Print.tcfo("水開了");} catch (InterruptedException e){Print.tcfo(" 發生異常被中斷.");return false;}Print.tcfo(" 運行結束.");return true;}}static class WashJob implements Callable<Boolean>{@Overridepublic Boolean call() throws Exception{try{Print.tcfo("洗茶壺");Print.tcfo("洗茶杯");Print.tcfo("拿茶葉");//線程睡眠一段時間,代表清洗中Thread.sleep(SLEEP_GAP);Print.tcfo("洗完了");} catch (InterruptedException e){Print.tcfo(" 清洗工作 發生異常被中斷.");return false;}Print.tcfo(" 清洗工作 運行結束.");return true;}}static boolean warterOk = false;static boolean cupOk = false;public synchronized static void drinkTea(){if (warterOk && cupOk){Print.tcfo("泡茶喝");}else if (!warterOk){Print.tcfo("燒水失敗,沒有茶喝了");}else if (!cupOk){Print.tcfo("杯子洗不了,沒有茶喝了");}}public static void main(String args[]){Thread.currentThread().setName("主線程");Callable<Boolean> hJob = new HotWarterJob();//③Callable<Boolean> wJob = new WashJob();//③//java 線程池ExecutorService jPool =Executors.newFixedThreadPool(10);//guava 線程池ListeningExecutorService gPool =MoreExecutors.listeningDecorator(jPool);ListenableFuture<Boolean> hFuture = gPool.submit(hJob);Futures.addCallback(hFuture, new FutureCallback<Boolean>(){public void onSuccess(Boolean r){if (r){warterOk = true;drinkTea();}else{Print.tcfo("燒水失敗,沒有茶喝了");}}public void onFailure(Throwable t){Print.tcfo("燒水失敗,沒有茶喝了");}});ListenableFuture<Boolean> wFuture = gPool.submit(wJob);Futures.addCallback(wFuture, new FutureCallback<Boolean>(){public void onSuccess(Boolean r){if (r){cupOk = true;drinkTea();}else{Print.tcfo("清洗失敗,沒有茶喝了");}}public void onFailure(Throwable t){Print.tcfo("杯子洗不了,沒有茶喝了");}});try{Print.tcfo("讀書中......");Thread.sleep(100000);} catch (InterruptedException e){Print.tcfo(getCurThreadName() + "發生異常被中斷.");}Print.tcfo(getCurThreadName() + " 運行結束.");gPool.shutdown();} }

總結

以上是生活随笔為你收集整理的【JUC系列】Future异步回调模式的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。