并行設(shè)計(jì)模式(一)-- Future模式

Java多線程編程中届囚,常用的多線程設(shè)計(jì)模式包括:Future模式免胃、Master-Worker模式痹栖、Guarded Suspeionsion模式蛙奖、不變模式和生產(chǎn)者-消費(fèi)者模式等潘酗。這篇文章主要講述Future模式杆兵,關(guān)于其他多線程設(shè)計(jì)模式的地址如下:
  關(guān)于Master-Worker模式的詳解: 并行設(shè)計(jì)模式(二)-- Master-Worker模式
  關(guān)于Guarded Suspeionsion模式的詳解: 并行設(shè)計(jì)模式(三)-- Guarded Suspeionsion模式
  關(guān)于不變模式的詳解: 并行設(shè)計(jì)模式(四)-- 不變模式
  關(guān)于生產(chǎn)者-消費(fèi)者模式的詳解:并行設(shè)計(jì)模式(五)-- 生產(chǎn)者-消費(fèi)者模式

  1. Future模式
    Future模式的核心在于:去除了主函數(shù)的等待時(shí)間雁仲,并使得原本需要等待的時(shí)間段可以用于處理其他業(yè)務(wù)邏輯。

Future模式有點(diǎn)類似于商品訂單琐脏。在網(wǎng)上購物時(shí)攒砖,提交訂單后缸兔,在收貨的這段時(shí)間里無需一直在家里等候,可以先干別的事情吹艇。類推到程序設(shè)計(jì)中時(shí)惰蜜,當(dāng)提交請求時(shí),期望得到答復(fù)時(shí)受神,如果這個(gè)答復(fù)可能很慢抛猖。傳統(tǒng)的是一直持續(xù)等待直到這個(gè)答復(fù)收到之后再去做別的事情,但如果利用Future模式鼻听,其調(diào)用方式改為異步财著,而原先等待返回的時(shí)間段,在主調(diào)用函數(shù)中撑碴,則可以用于處理其他事務(wù)撑教。

例如如下的請求調(diào)用過程時(shí)序圖。當(dāng)call請求發(fā)出時(shí)醉拓,需要很長的時(shí)間才能返回伟姐。左邊的圖需要一直等待,等返回?cái)?shù)據(jù)后才能繼續(xù)其他操作亿卤;而右邊的Future模式的圖中客戶端則無需等到可以做其他的事情愤兵。服務(wù)器段接收到請求后立即返回結(jié)果給客戶端,這個(gè)結(jié)果并不是真實(shí)的結(jié)果(是虛擬的結(jié)果)排吴,也就是先獲得一個(gè)假數(shù)據(jù)恐似,然后執(zhí)行其他操作。


圖片.png

Future模式的主要參與者如下表所示:


圖片.png
  1. Future模式的代碼實(shí)現(xiàn)


    圖片.png

    <1. Main函數(shù)的實(shí)現(xiàn)

Main函數(shù)主要負(fù)責(zé)調(diào)用Client發(fā)起請求傍念,并使用返回的數(shù)據(jù):

public class Main {
    public static void main(String[] args) {
        Client client = new Client();
        // 這里會立即返回矫夷,因?yàn)楂@取的是FutureData,而非RealData
        Data data = client.request("name");
        System.out.println("請求完畢");

        try {
            // 這里可以用一個(gè)sleep代替對其他業(yè)務(wù)邏輯的處理
            // 在處理這些業(yè)務(wù)邏輯過程中憋槐,RealData也正在創(chuàng)建双藕,從而充分了利用等待時(shí)間
            Thread.sleep(2000);

            // 使用真實(shí)數(shù)據(jù)
            System.out.println("數(shù)據(jù)=" + data.getResult());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

<2. Client的實(shí)現(xiàn)

Client主要實(shí)現(xiàn)了獲取futrueData,開啟構(gòu)造RealData的線程阳仔,并在接受請求后忧陪,很快地返回FutureData

public class Client {
    public Data request(final String string) {
        final FutureData futureData = new FutureData();

        new Thread(new Runnable() {
            @Override
            public void run() {
                // RealData的構(gòu)建很慢,所以放在單獨(dú)的線程中運(yùn)行
                RealData realData = new RealData(string);
                futureData.setRealData(realData);
            }
        }).start();
        return futureData; // 先直接返回FutureData
    }
}

<3. Data的實(shí)現(xiàn)

Data是一個(gè)接口近范,提供了getResult()方法嘶摊。無論futureData或者RealData都實(shí)現(xiàn)了這個(gè)接口

public interface Data {
    String getResult() throws InterruptedException;
}

<4. FutureData的實(shí)現(xiàn)

FutureData實(shí)現(xiàn)了一個(gè)快速返回的RealData包裝。它只是一個(gè)包裝评矩,或者說是一個(gè)RealData的虛擬實(shí)現(xiàn)叶堆。因此,它可以很快被構(gòu)造并返回斥杜。當(dāng)使用FutureData的getResult()方法是虱颗,程序會阻塞沥匈,等待RealData被注入到程序中,才使用RealData的getResult()方法返回忘渔。

public class FutureData implements Data {
    RealData realData = null; // FutureData是RealData的封裝
    boolean isReady = false; // 是否已經(jīng)準(zhǔn)備好

    public synchronized void setRealData(RealData realData) {
        if (isReady)
            return;
        this.realData = realData;
        isReady = true;
        notifyAll(); // RealData已經(jīng)被注入到FutureData中了高帖,通知getResult()方法
    }

    @Override
    public String getResult() throws InterruptedException {
        if (!isReady) {
            wait(); // 一直等到RealData注入到FutureData中
        }
        return realData.getResult();
    }
}

<5. RealData的實(shí)現(xiàn)

RealData是最終需要使用的數(shù)據(jù)模型,它的構(gòu)造很慢畦粮。在這里散址,使用sleep()函數(shù)模擬這個(gè)過程

public class RealData implements Data {
    protected String data;

    public RealData(String data) {
        // 利用sleep方法來表示RealData構(gòu)造過程是非常緩慢的
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.data = data;
    }

    @Override
    public String getResult() throws InterruptedException {
        return data;
    }
}
  1. JDK的內(nèi)置Future模式實(shí)現(xiàn)
     由于Future是非常常用的多線程設(shè)計(jì)模式,因此在JDK中內(nèi)置了Future模式的實(shí)現(xiàn)宣赔。這些類在java.util.concurrent包里面爪飘。其中最為重要的是FutureTask類,它實(shí)現(xiàn)了Runnable接口拉背,作為單獨(dú)的線程運(yùn)行师崎。在其run()方法中,通過Sync內(nèi)部類調(diào)用Callable接口椅棺,并維護(hù)Callable接口的返回對象犁罩。當(dāng)使用FutureTask.get()方法時(shí),將返回Callable接口的返回對象两疚。其核心結(jié)構(gòu)圖如下所示:


    圖片.png

JDK內(nèi)置的Future模式功能強(qiáng)大床估,除了基本的功能外,它還可以取消Future任務(wù)诱渤,或者設(shè)定future任務(wù)的超時(shí)時(shí)間丐巫。Callable接口是一個(gè)用戶自定義的實(shí)現(xiàn)。在應(yīng)用程序中勺美,通過實(shí)現(xiàn)Callable接口的call()方法递胧,指定FutureTask的實(shí)際工作內(nèi)容和返回對象。

Future接口提供的線程控制功能有:

1 boolean cancle(boolean mayInterruptIfRunning);  // 取消任務(wù)
2 boolean isCancelled();  // 是否已經(jīng)取消
3 boolean isDone();    // 是否已經(jīng)完成
4 V get() throws InterruptedException, ExecutionException;  //取得返回對象
5 V get(long timeout, TimeUnit unit);  //取得返回對象赡茸,可以設(shè)置超時(shí)時(shí)間

同樣缎脾,針對上述的實(shí)例,如果使用JDK自帶的實(shí)現(xiàn)占卧,則需要作一些調(diào)整遗菠。

首先,需要實(shí)現(xiàn)Callable接口华蜒,實(shí)現(xiàn)具體的業(yè)務(wù)邏輯辙纬。在本例中,依然使用RealData來實(shí)現(xiàn)這個(gè)接口:

 1 public class RealData implements Callable<String> {
 2     private String para;
 3 
 4     public RealData(String para) {
 5         this.para = para;
 6     }
 7 
 8     @Override
 9     public String call() throws Exception {
10         // 利用sleep方法來表示真是業(yè)務(wù)是非常緩慢的
11         StringBuffer sb = new StringBuffer();
12         for (int i = 0; i < 10; i++) {
13             sb.append(para);
14             try {
15                 Thread.sleep(1000);
16             } catch (InterruptedException e) {
17                 e.printStackTrace();
18             }
19         }
20         return sb.toString();
21     }
22 }

在這個(gè)改進(jìn)中叭喜,RealData的構(gòu)造變動非澈丶穑快,因?yàn)槠渲饕獦I(yè)務(wù)邏輯被移動到call()方法內(nèi)域滥,并通過call()方法返回纵柿。

Main方法修改如下,由于使用了JDK的內(nèi)置框架启绰,Data昂儒、FutureData等對象就不再需要了。在Main方法的實(shí)現(xiàn)中委可,直接通過RealData構(gòu)造FutureTask渊跋,并將其作為單獨(dú)的線程運(yùn)行。在提交請求后着倾,執(zhí)行其他業(yè)務(wù)邏輯拾酝,最后通過FutureTask.get()方法,得到RealData的執(zhí)行結(jié)果卡者。

1 public class Main {
 2     public static void main(String[] args) {
 3         FutureTask<String> future = new FutureTask<String>(new RealData("liangyongxing"));
 4         ExecutorService executor = Executors.newFixedThreadPool(1);    // 使用線程池
 5         //執(zhí)行FutureTask蒿囤,相當(dāng)于上例中的client.request("name")發(fā)送請求
 6         //在這里開啟線程進(jìn)行RealData的call()執(zhí)行
 7         executor.submit(future);
 8         System.out.println("請求完畢");
 9 
10         try {
11             // 這里仍然可以做額外的數(shù)據(jù)操作,這里使用sleep代替其他業(yè)務(wù)邏輯的處理
12             Thread.sleep(2000);
13             
14             /**
15              * 相當(dāng)于上例當(dāng)中的 data.getResult()崇决,取得call()方法的返回值
16              * 如果此時(shí)call()方法沒有執(zhí)行完畢材诽,則依然會等待
17              */
18             System.out.println("數(shù)據(jù) = " + future.get());
19         } catch (InterruptedException | ExecutionException e) {
20             e.printStackTrace();
21         } finally {
              executor.shutdown();
          }
22     }
23 }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市恒傻,隨后出現(xiàn)的幾起案子脸侥,更是在濱河造成了極大的恐慌,老刑警劉巖盈厘,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件睁枕,死亡現(xiàn)場離奇詭異,居然都是意外死亡沸手,警方通過查閱死者的電腦和手機(jī)外遇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來契吉,“玉大人臀规,你說我怎么就攤上這事≌ひ” “怎么了塔嬉?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長租悄。 經(jīng)常有香客問我谨究,道長,這世上最難降的妖魔是什么泣棋? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任胶哲,我火速辦了婚禮,結(jié)果婚禮上潭辈,老公的妹妹穿的比我還像新娘鸯屿。我一直安慰自己澈吨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布寄摆。 她就那樣靜靜地躺著谅辣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪婶恼。 梳的紋絲不亂的頭發(fā)上桑阶,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機(jī)與錄音勾邦,去河邊找鬼蚣录。 笑死,一個(gè)胖子當(dāng)著我的面吹牛眷篇,可吹牛的內(nèi)容都是我干的萎河。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼蕉饼,長吁一口氣:“原來是場噩夢啊……” “哼公壤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起椎椰,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤厦幅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后慨飘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體确憨,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年瓤的,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了休弃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡圈膏,死狀恐怖塔猾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情稽坤,我是刑警寧澤丈甸,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站尿褪,受9級特大地震影響睦擂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜杖玲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一顿仇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦臼闻、人聲如沸鸿吆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惩淳。三九已至,卻和暖如春市埋,著一層夾襖步出監(jiān)牢的瞬間黎泣,已是汗流浹背恕刘。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工缤谎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人褐着。 一個(gè)月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓坷澡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親含蓉。 傳聞我的和親對象是個(gè)殘疾皇子频敛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評論 2 355

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)馅扣,斷路器斟赚,智...
    卡卡羅2017閱讀 134,665評論 18 139
  • 當(dāng)我們創(chuàng)建一個(gè)線程時(shí),我們想獲取線程運(yùn)行完成后的結(jié)果差油,一般使用回調(diào)的方式拗军。例如: 運(yùn)行結(jié)果: 這種方式的實(shí)現(xiàn)有三個(gè)...
    wo883721閱讀 5,715評論 2 9
  • Java-Review-Note——4.多線程 標(biāo)簽: JavaStudy PS:本來是分開三篇的,后來想想還是整...
    coder_pig閱讀 1,653評論 2 17
  • 1.官網(wǎng)下載最新的Git(https://git-scm.com/download/): 2.下載好以后安裝git...
    給你一顆小瓜子閱讀 42,076評論 2 11
  • 總是留不住時(shí)光的腳步蓄喇,驀然回首发侵,發(fā)現(xiàn),回憶依舊妆偏,秋葉早已飄落刃鳄,翻看那如歷史畫卷般的相冊,照片早已泛黃褪色钱骂,但是那里...
    墻頭花閱讀 183評論 1 2