1. Future模式核心思想
Future模式的核心在于:去除了主函數(shù)的等待時間,并使得原本需要等待的時間段可以用于處理其他業(yè)務邏輯(根據(jù)《Java程序性能優(yōu)化》)。
Future模式有點類似于商品訂單。在網(wǎng)上購物時,提交訂單后,在收貨的這段時間里無需一直在家里等候,可以先干別的事情。類推到程序設計中時,當提交請求時,期望得到答復時,如果這個答復可能很慢。傳統(tǒng)的時一直等待到這個答復收到時再去做別的事情,但如果利用Future設計模式就無需等待答復的到來,在等待答復的過程中可以干其他事情。
例如如下的請求調用過程時序圖。當call請求發(fā)出時,需要很長的時間才能返回。左邊的圖需要一直等待,等返回數(shù)據(jù)后才能繼續(xù)其他操作;而右邊的Future模式的圖中客戶端則無需等到可以做其他的事情。服務器段接收到請求后立即返回結果給客戶端,這個結果并不是真實的結果(是虛擬的結果),也就是先獲得一個假數(shù)據(jù),然后執(zhí)行其他操作。

2. Future模式Java實現(xiàn)
Client的實現(xiàn)
Client主要完成的功能包括:1. 返回一個FutureData;2.開啟一個線程用于構造RealData。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Client {
public Data request(final String string) {
final FutureData futureData = new FutureData();
new Thread(new Runnable() {
@Override
public void run() {
//RealData的構建很慢,所以放在單獨的線程中運行
RealData realData = new RealData(string);
futureData.setRealData(realData);
}
}).start();
return futureData; //先直接返回FutureData
}
}
|
Data的實現(xiàn)
無論是FutureData還是RealData都實現(xiàn)該接口。
1 2 3 | public interface Data {
String getResult() throws InterruptedException;
}
|
FutureData的實現(xiàn)
FutureData是Future模式的關鍵,它實際上是真實數(shù)據(jù)RealData的代理,封裝了獲取RealData的等待過程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | //FutureData是Future模式的關鍵,它實際上是真實數(shù)據(jù)RealData的代理,封裝了獲取RealData的等待過程
public class FutureData implements Data {
RealData realData = null; //FutureData是RealData的封裝
boolean isReady = false; //是否已經(jīng)準備好
public synchronized void setRealData(RealData realData) {
if(isReady)
return;
this.realData = realData;
isReady = true;
notifyAll(); //RealData已經(jīng)被注入到FutureData中了,通知getResult()方法
}
@Override
public synchronized String getResult() throws InterruptedException {
if(!isReady) {
wait(); //一直等到RealData注入到FutureData中
}
return realData.getResult();
}
}
|
RealData的實現(xiàn)
RealData是最終需要使用的數(shù)據(jù),它的構造函數(shù)很慢。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class RealData implements Data {
protected String data;
public RealData(String data) {
//利用sleep方法來表示RealData構造過程是非常緩慢的
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.data = data;
}
@Override
public String getResult() {
return data;
}
}
|
測試運行
主函數(shù)主要負責調用Client發(fā)起請求,并使用返回的數(shù)據(jù)。
1 2 3 4 5 6 7 8 9 10 11 12 | public class Application {
public static void main(String[] args) throws InterruptedException {
Client client = new Client();
//這里會立即返回,因為獲取的是FutureData,而非RealData
Data data = client.request("name");
//這里可以用一個sleep代替對其他業(yè)務邏輯的處理
//在處理這些業(yè)務邏輯過程中,RealData也正在創(chuàng)建,從而充分了利用等待時間
Thread.sleep(2000);
//使用真實數(shù)據(jù)
System.out.println("數(shù)據(jù)="+data.getResult());
}
}
|
3. Future模式的JDK內置實現(xiàn)
由于Future是非常常用的多線程設計模式,因此在JDK中內置了Future模式的實現(xiàn)。這些類在java.util.concurrent包里面。其中最為重要的是FutureTask類,它實現(xiàn)了Runnable接口,作為單獨的線程運行。在其run()方法中,通過Sync內部類調用Callable接口,并維護Callable接口的返回對象。當使用FutureTask.get()方法時,將返回Callable接口的返回對象。同樣,針對上述的實例,如果使用JDK自帶的實現(xiàn),則需要作如下調整。
首先,Data接口和FutureData就不需要了,JDK幫我們實現(xiàn)了。
其次,RealData改為這樣:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import java.util.concurrent.Callable;
public class RealData implements Callable<string> {
protected String data;
public RealData(String data) {
this.data = data;
}
@Override
public String call() throws Exception {
//利用sleep方法來表示真是業(yè)務是非常緩慢的
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return data;
}
}</string>
|
最后,在測試運行時,這樣調用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
public class Application {
public static void main(String[] args) throws Exception {
FutureTask<string> futureTask =
new FutureTask<string>(new RealData("name"));
ExecutorService executor =
Executors.newFixedThreadPool(1); //使用線程池
//執(zhí)行FutureTask,相當于上例中的client.request("name")發(fā)送請求
executor.submit(futureTask);
//這里可以用一個sleep代替對其他業(yè)務邏輯的處理
//在處理這些業(yè)務邏輯過程中,RealData也正在創(chuàng)建,從而充分了利用等待時間
Thread.sleep(2000);
//使用真實數(shù)據(jù)
//如果call()沒有執(zhí)行完成依然會等待
System.out.println("數(shù)據(jù)=" + futureTask.get());
}
}</string></string>
|
|