1. 程式人生 > >Java併發模式—Future模式

Java併發模式—Future模式

Future

Future模式是多執行緒開發中非常常見的一種設計模式,它的核心思想是非同步呼叫。當我們需要呼叫一個函式方法時,如果這個函式執行很慢,那麼我們就要進行等待。但有時候,我們可能不急著要結果。因此,我們可以讓被調者立即返回,讓它在後臺慢慢處理這個請求。對於呼叫者來說,則可以先處理一些其他任務,在真正需要資料的場合再去嘗試獲得需要的資料。

對於Future模式來說,雖然它無法立即給出你需要的資料。但是,它會返回給你一個契約,將來,你可以憑藉這個契約去重新獲取你需要的資訊。

Future的簡易實現

參與者 作用
Main 系統啟動,呼叫Client發出請求
Client 返回Data物件,立即返回FutureData,並開啟ClientThread執行緒裝配RealData
Data 返回資料的介面
FutureData Future資料,構造很快,但是是一個虛擬的過程,需要裝配RealData
RealData 真實資料,其構造是比較慢的

下面是data介面

public interface Data {
    public String getResult();
}

FutureData:

public class FutureData implements Data{
    protected RealData realData =
null; protected boolean isReady = false; public synchronized void setRealData(RealData realData){ if(isReady){ return; } this.realData = realData; isReady = true; notifyAll(); } @Override public synchronized String getResult() {
while (!isReady){ try{ wait(); }catch (InterruptedException e) { e.printStackTrace(); } } return realData.result; } }

RealData

public class RealData implements Data{
    protected final String result;
    public RealData(String para){
        //RealData的構造可能很慢,需要使用者等待很久,這裡用seelp模擬
        StringBuffer sb = new StringBuffer();
        for(int i = 0; i < 10; i++){
            sb.append(para);
        }
        try{
            //這裡用sleep,代替一個很慢的操作過程
            Thread.sleep(100);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        result = sb.toString();
    }

    @Override
    public String getResult() {
        return result;
    }
}

Client類:

public class Client {
    public Data request(final String queryStr){
        final FutureData future = new FutureData();
        new Thread(){
            public void run(){                    //RealData的構建很慢,所以在單獨執行緒中進行
                RealData realData = new RealData(queryStr);
                future.setRealData(realData);
            }
        }.start();
        return future;
    }
}

Main函式

public class Main {
    public static void main(String[] args){
        Client client = new Client();
        //這裡會立即返回,因為得到的是FutureData而不是RealData
        Data data= client.request("name");
        System.out.println("請求完畢");
        try{
            //這裡可以用一個sleep代替了對其他業務邏輯的處理
            //在處理這些業務邏輯的過程中,RealData被建立,從而充分利用了等待時間
            Thread.sleep(2000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("資料= " + data.getResult());
    }

}

JDK中的Future模式

p1

可以通過Future介面來得到真實的資料。RunnableFuture繼承了Future和Ruuable介面,其中run()方法用於構造真實的資料。它有一個具體的實現FutureTask類。FutureTask有一個內部類Sync,一些實質性的工作,會委託Sync類實現。而Sync類最終會呼叫Callable介面,完成實際資料的組裝工作。

具體使用

RealData

package JDKFuture;

import java.util.concurrent.Callable;

public class RealData implements Callable<String> {
    private String para;
    public RealData(String para){
        this.para = para;
    }
    @Override
    public String call() throws Exception{
        StringBuffer sb = new StringBuffer();
        for(int i = 0 ; i < 10; i++){
            sb.append(para);
            try{
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        return sb.toString();
    }
}

FutureMain

package JDKFuture;

import org.omg.Messaging.SYNC_WITH_TRANSPORT;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class FutureMain {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
       //構造FutureTask
        FutureTask<String> future = new FutureTask<String>(new RealData("a"));
        ExecutorService executor = Executors.newFixedThreadPool(1);
        //執行FutureTask,相當與上例中的client.request("a")傳送請求
        //在這裡開啟執行緒進行RealData的call()執行
        executor.submit(future);

        System.out.println("請求完畢");
        try{
            //這裡依然可以做額外操作,這裡使用sleep代替其他業務邏輯的處理
            Thread.sleep(2000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        //相當於data.getResult(),取得call()方法的返回值
        //如果此時call()方法沒有執行完成,則依然會等待
        System.out.println("資料 = " + future.get());
    }
}

除了基本功能wait,JDK還為Future介面提供了一些簡單的控制功能。

boolean cancel(boolean mayInterruptIfRunning);				//取消任務
boolean isCancelled();										//是否已經取消
boolean isDone();											//是否已完成
V get() throws InterruptedException,ExecutionException; 	//取得返回物件
V get(long timeout,TimeUnit unit)							//取得返回物件,可以設定超時時間