Future模式

_吹雪_發表於2018-09-29

1. Future模式簡介

Future模式就是,當某一程式提交請求,期望得到一個答覆。但是可能伺服器程式對這個請求的處理比較慢,因此不可能馬上收到答覆。但是,在傳統的單執行緒環境下,呼叫函式是同步的,它必須等到服務程式返回結果,才能繼續進行其他處理。而Future模式下,呼叫方法是非同步的,原本等待返回的時間段,在主調函式中,則可以處理其他的任務。傳統的序列程式如下圖所示:

https://images2015.cnblogs.com/blog/805129/201603/805129-20160317205509693-742725053.png

Future模式的處理流程:

https://images2015.cnblogs.com/blog/805129/201603/805129-20160317205558271-518468971.png

從圖中可以看出,雖然call()本身是一個需要很長世間處理的程式。但是,服務程式不等資料處理完就立刻返回客戶端一個偽資料,實現Future模式的客戶端在拿到這個返回結果後,並不急於對它進行處理,而是去呼叫其它的業務邏輯,使call()方法有充分的時間去處理完成,這也是Future模式的精髓所在。 在處理完其他業務邏輯後,最後再使用處理比較費時的Future資料。這個在處理過程中,就不存在無謂的等待,充分利用了時間,從而提升了系統的響應和效能。

2. Future模式的核心結構

下面以一個經典的Future實現為例,簡單介紹下Future的核心實現。程式碼中

  • Data介面:返回資料的介面;
  • FutureData類:實現Data介面,構造很快,返回一個虛擬的偽資料,需要裝配RealData;
  • RealData類:實現Data介面,返回真實資料,構造比較慢;
  • Client:返回Data資料,立即返回FutureData資料,並開啟執行緒裝配RealData資料。
public interface Data {
    public String getResult();
}

public class FutureData implements Data {

    protected RealData realData = null;

    protected boolean isReady = false;
    //進行同步控制
    public synchronized void setResult(RealData realData){
        if(isReady){
            return;
        }
        System.out.println("FutureData.setResult()");
        this.realData=realData;
        isReady = true;
        notifyAll();

    }
    //實際呼叫返回RealDate的資料
    @Override
    public synchronized String getResult() {
        while(!isReady){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("FutureData.getResult()");
        return realData.result;
    }

public class RealData implements Data{

    protected final String result;

    public RealData(String s) {
        StringBuffer sb = new StringBuffer();

        for (int i = 0; i < 10; i++) {
            sb.append(s);
            try {
                //模擬構造時間比較長
                Thread.sleep(1000);
            } catch (InterruptedException e) {

            }

        }

        System.out.println("RealData.RealData()");
        result = sb.toString();
    }

public class Client {
    public Data request(final String queryStr){
                //返回偽資料
        final FutureData futureData = new FutureData();
                //開啟執行緒構造真實資料
        new Thread(){
            public void run(){
                RealData realData = new RealData(queryStr);
                futureData.setResult(realData);
            }
        }.start();
        //返回偽資料,等待真實資料載入
        return futureData;
    }
}

啟動系統,呼叫Client傳送請求:

public class TestMain {
    public static void main(String[] args) {
        Data data = new Client().request("123456");
        System.out.println(data);
        System.out.println(data.getResult());
    }
}

可以看出,FutureData是Future模式實現的關鍵,它實際是真實資料RealData的代理,封裝了獲取RealDate的等待過程.

3. JDK內建實現

在JDK的內建併發包中,就已經內建了一種Future的實現,提供了更加豐富的執行緒控制,其基本用意和核心理念與上面實現程式碼一致。

在JDK中的Future模式中,最重要的是FutureTask類,它實現了Runnable介面,可以作為單獨的執行緒執行。在其run()方法中,通過Sync內部類,呼叫Callable介面,並維護Callable介面的返回物件。當使用FutureTask.get()時,將返回Callable介面的返回物件。FutureTask還可以對任務本身進行其他控制操作。

利用Callable介面實現上述例子相同的操作:

RealData類的實現:

public class Real1Data implements Callable<String>{

    private String reaString;

    public Real1Data(String reaString) {
        super();
        this.reaString = reaString;
    }

  
    @Override
    public String call() throws Exception {

        StringBuffer sb = new StringBuffer();

        for (int i = 0; i < 10; i++) {
            sb.append(reaString);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO: handle exception
            }

        }

        return sb.toString();
    }

}

Client程式碼實現:

public class Test1Main {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        FutureTask<String> future =  new FutureTask<>(new Real1Data("1111"));

        ExecutorService exe = Executors.newFixedThreadPool(1);

        exe.submit(future);

        System.out.println("FutureTask");

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("FutureTask"+future.get());
    }
}

可以看出RealData的構造速度很快,其核心程式碼邏輯放在了call()中實現,不再需要Data和FutureData,直接通過RealData來構造FutureTask,將其作為單獨的執行緒執行。在提交請求後,執行其他業務邏輯,做好通過FututeTask.get()方法,得到RealData的執行結果。

Future模式核心在於去除了主呼叫函式的等待時間,並使得原本需要等待的時間可以充分利用來處理其他業務邏輯,充分的利用了系統資源。

參考:
https://www.cnblogs.com/lcngu/p/5289605.html
https://www.jianshu.com/p/949d44f3d9e3

相關文章