深入理解「Future模式」原理與技術

作者:像風一樣i
來源:https://www.cnblogs.com/yueshutong/p/9687662.html
深入理解「Future模式」原理與技術

1.Future模式

Future模式和多線程技術密切相關,可以說是利用多線程技術優化程序的一個實例。

在程序設計中,當某一段程序提交了一個請求,期望得到一個答覆。但非常不幸的是,服務程序對這個請求的處理可能比較慢,比如,這個請求可能是通過互聯網、HTTP或者Web Service等並不高效的方式調用的。在傳統的單線程環境下,調用函數是同步的,也就是說它必須等到服務程序返回結束後,才能進行其他處理。而在Future模式下,調用方式改為異步的,而原先等待返回的時間段,在主調用函數中,則可能用於處理其它事務。下面我們嘗試實現這一功能:

1)Main方法的實現

main方法主要負責調用Client發起請求,並使用返回的數據:

public class Future {
public static void main(String[] args) {
Client client = new Client();
Data data = client.request("name");
System.out.println("請求完畢 "+System.currentTimeMillis());
//...這裡做一些其它任務
System.out.println("數據:"+data.getResult());
System.out.println("獲取完畢 "+System.currentTimeMillis());
}

}

2)Client的實現

client主要實現了獲取FutureData,開啟構造RealData的線程,並在接受請求後,很快的返回FutureData。

public class Client {
public Data request(String queryStr){
FutureData futureData = new FutureData();
new Thread(new Runnable() {
@Override
public void run() {
RealData realData = new RealData(queryStr);
futureData.setRealData(realData);
}
}).start();
return futureData;
}
}

3)Data的實現

Data是一個接口,提供了getResult()方法。

public interface Data {
String getResult();
}

4)FutureData的實現

FutureData 實現了一個快速返回的RealData 包裝。它只是一個包裝,或者說是一個RealData 的虛擬實現 。因此,它可以很快被構造並返回。當使用FutureData的getResult()方法時,程序會阻塞,等待RealData()被注入到程序中,才使用RealData的getResult()方法返回。

public class FutureData implements Data {
private RealData realData = null;

private boolean isReady = false;
synchronized public void setRealData(RealData realData){
if (isReady){
return;
}
this.realData = realData;
isReady = true;
notifyAll(); //通知所有等待的線程繼續運行
}
@Override
synchronized public String getResult() {
while (!isReady){
try {
System.out.print("...waiting...");
wait(); //使當前線程在此處進行等待,直到被通知後繼續運行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return realData.result;
}
}

5)RealData 的實現

RealData 是最終需要使用的數據模型,它的構造很慢。在這裡,使用sleep()函數模擬這個過程。

public class RealData implements Data {
protected String result;
public RealData(String para) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
result = "["+para+"]";
}
@Override
public String getResult() {
return result;
}
}

運行結果:

請求完畢 1537520554813
...waiting...
數據:[name]
獲取完畢 1537520555890

程序運行的流程是Main線程去獲取數據,但是數據還在處理中,於是Main線程進入等待狀態,當數據處理完並通知等待所有等待的線程之後,Main線程得以繼續運行下去。

2.JDK的內置實現

Future模式如此常用,以至於在JDK的併發包中,就已經內置了一種Future模式的實現了。

示例程序:

public class RealData implements Callable {
private String para;
public RealData(String para) {
this.para = para;
}
@Override
public String call() throws Exception {
//這裡是真實的業務邏輯
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "["+para+"]";
}

public static void main(String[] args) throws ExecutionException, InterruptedException {
//傳入RealData到FutureTask
FutureTask futureTask = new FutureTask(new RealData("name"));
//創建一個線程池
ExecutorService executorService = Executors.newFixedThreadPool(1);
//在這裡開啟線程執行RealData的call()方法
executorService.submit(futureTask);
System.out.println("請求完畢 "+System.currentTimeMillis());
//...這裡進行一些其它操作
System.out.println("數據:"+futureTask.get());
System.out.println("獲取完畢 "+System.currentTimeMillis());
//啟動一個有序的關閉,之前提交的任務將被執行,但是不會接受新的任務。
executorService.shutdown();
}

}

運行結果:

請求完畢 1537521833970
數據:[name]
獲取完畢 1537521834977

Callable接口是一個用戶自定義實現的接口。在應用程序中,通過實現Callable接口的call()方法,指定FutureTask的實際內容和返回對象。

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

//取消任務 

boolean cancel(boolean mayInterruptIfRunning)
//是否已經取消
boolean isCancelled()
//是否已經完成
boolean isDone()
//取得返回對象
V get() throws InterruptedException, ExecutionException
//取得返回對象,可以設置超時時間
V get(long timeout, TimeUnit unit)

尾聲

Future模式的核心在於使用多線程技術去除了主函數中的等待時間,並使得原來需要等待的時間段可以用於處理其他的業務邏輯,從而充分利用計算機資源。


分享到:


相關文章: