java多線程之Future模式

一、Future模式

Future的意思是未來、期貨,假設有一個方法需要花費很長時間才能獲取運行結果,那麼,與其一直等待結果,不如先拿一張“提貨單”,獲取提貨單並不耗費時間,這裏的“提貨單”我們就成爲Future角色。

二、示例程序

在Thread-Per-Meaasge模式中,我們知道在每次發出請求時都創建一個線程的示例程序,其中,程序中沒有返回值。如:

new Thread() {              public void run() {                  helper.handle(count, c);              }          }.start();

在Future模式中,程序一旦發出請求,就會立即獲得返回值,也就說,會有如下的返回值:

Data data = host.request(10, 'A');

這裏的返回值data並非請求的運行結果,爲了獲得請求的運行結果。通過線程會調用data的getContent方法去獲取運行結果:

data.getContent();

1.類和接口的一覽表

類名 說明
Main.java 向Host發出請求並獲取數據的類
Host.java 向請求返回FutureData的實例的類
Data.java 表示訪問數據的方法的接口,由FutureData和RealData實現該接口
FutureData.java 表示RealData的“提貨單”的類,其他線程會創建RealData的實例
RealData.java 表示實際數據的類,構造函數的處理會花費很長時間

2.示例程序的類圖

img

3.實例程序的時序圖

img

4.Main.java

package com.viagra.Future_Pattern.Lesson1;

/**
 * @Auther: viagra
 * @Date: 2019/11/19 19:49
 * @Description:
 */
public class Main {
    public static void main(String[] args) {
        /**
         * Main類會調用request方法三次,接着它會接收三個Data(data1、data2、data3)作爲返回值
         * 這三個返回值實際上都是FutureData的實例,無需花費時間即可獲取它們,類似蛋糕的提貨單
         * 然後爲了表示Main類去執行了其他操作,我們讓其sleep2s,接下來,分別調用之前接收到的返回值data1、data2、data3的getContent方法來獲取真正希望獲取的結果。
         */
        System.out.println("Main Begin");
        Host host = new Host();
        Data data1 = host.request(10, 'A');
        Data data2 = host.request(20, 'B');
        Data data3 = host.request(30, 'C');
        System.out.println("Main otherJob Begin");

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("data1 = " + data1.getContent());
        System.out.println("data2 = " + data2.getContent());
        System.out.println("data3 = " + data3.getContent());
        System.out.println("Main End");
    }
}

5.Host.java

package com.viagra.Future_Pattern.Lesson1;

/**
 * @Auther: viagra
 * @Date: 2019/11/19 19:49
 * @Description:
 */
public class Host {
    /**
     * 首先會創建FutureData的實例,接着啓動一個新的線程並在新線程中創建RealData的實例。
     * 執行request的線程會做一下三件事情:
     * 1.創建FutureData的實例
     * 2.啓動一個新線程,用戶創建RealData的實例
     * 3.將FutureData的實例作爲返回值返回給調用者
     * @param count
     * @param c
     * @return
     */
    public Data request(final int count, final char c) {
        System.out.println("    request( " + count + " , " + c + " ), Begin");
        //(1)創建FutureData的實例
        final FutureData future = new FutureData();

        //(2)啓動一個新線程,用戶創建RealData的實例
        new Thread(){
            public void run(){
                RealData realData = new RealData(count ,c);
                future.setRealData(realData);
            }
        }.start();
        System.out.println("    request( " + count + " , " + c + " ) End");

        //(3)返回FutureData的實例
        return future;
    }
}

6.Data.java接口

package com.viagra.Future_Pattern.Lesson1;

/**
 * @Auther: viagra
 * @Date: 2019/11/19 19:49
 * @Description:
 */
public interface Data {
    /**
     * Data接口是表示訪問數據的方法接口,FurureData類和RealData類實現了該接口
     * @return
     */
    public abstract String getContent();
}

7.FutureData.java

package com.viagra.Future_Pattern.Lesson1;

/**
 * @Auther: viagra
 * @Date: 2019/11/19 19:49
 * @Description:
 */
public class FutureData implements Data {
    /**
     * readData字段是用戶保存稍後創建完畢的RealData的實例的字段。
     * ready字段是表示是否已經爲readData賦值的字段,如果爲true,表示已經爲realData賦值。
     * setRealData方法是用戶將RealData的實例賦值給realData字段的方法
     * setRealData方法會被Host類的request方法創建新的線程調用
     * getContent是用戶獲取實際數據的方法。
     */
    private RealData realData = null;
    private boolean ready = false;

    public synchronized void setRealData(RealData realData) {
        if (ready) {
            return;
        }
        this.realData = realData;
        this.ready = true;
        notifyAll();
    }

    public synchronized String getContent() {
        while (!ready) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return realData.getContent();
    }
}

8.RealData.java

package com.viagra.Future_Pattern.Lesson1;

/**
 * @Auther: viagra
 * @Date: 2019/11/19 19:49
 * @Description:
 */
public class RealData implements Data {
    /**
     * 是一個需要花費很長時間才能創建實例的類。
     * getContent方法的處理僅僅是返回content字段的內容
     */
    private final String content;

    public RealData(int count, char c) {
        System.out.println("    making RealData ( " + count + " , " + c + " ) Begin");
        char[] buffer = new char[count];
        for (int i = 0; i < count; i++) {
            buffer[i] = c;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("    making RealData ( " + count + " , " + c + " ) End");
        this.content = new String(buffer);
    }

    public String getContent() {
        return content;
    }
}

9.運行示例程序

Main Begin
    request( 10 , A ), Begin
    request( 10 , A ) End
    request( 20 , B ), Begin
    request( 20 , B ) End
    request( 30 , C ), Begin
    making RealData ( 10 , A ) Begin
    making RealData ( 30 , C ) Begin
    request( 30 , C ) End
Main otherJob Begin
    making RealData ( 20 , B ) Begin
    making RealData ( 10 , A ) End
data1 = AAAAAAAAAA
    making RealData ( 20 , B ) End
data2 = BBBBBBBBBBBBBBBBBBBB
    making RealData ( 30 , C ) End
data3 = CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
Main End

3.Future模式中的角色

1.Client(請求者)

Client角色向Host角色發出請求(request)並會立即接收到請求的處理結果(返回值),是實例程序中的Main類。

2.Host

Host角色會創建新的線程,並開始在新線程中創建ReakData角色,同時,它會將Future角色返回給Clinet角色,是示例程序中的Host類。

3.VirtualData(虛擬數據)

VirtualData角色是讓Future角色與RealData角色具有一致性的角色,是示例程序中的Data接口。

4.RealData(真實數據)

ReakData角色是表示真是數據的角色,是示例程序中的RealData類。

5.Futrue(期貨)

Future角色是RealData角色的“提貨者”,由Host角色傳遞給Client角色,從程序行爲上看,對Client角色而言,Future角色就是VirtureData角色。是示例程序中的FutureData類。

6.Future模式的類圖

img
代碼案例

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章