Java多執行緒程式設計中Future模式的詳解
Java多執行緒程式設計中,常用的多執行緒設計模式包括:Future模式、Master-Worker模式、Guarded Suspeionsion模式、不變模式和生產者-消費者模式等。這篇文章主要講述Future模式,關於其他多執行緒設計模式的地址如下:
關於其他多執行緒設計模式的地址如下:
關於Master-Worker模式的詳解: Java多執行緒程式設計中Master-Worker模式的詳解
關於Guarded Suspeionsion模式的詳解: Java多執行緒程式設計中Guarded Suspeionsion模式的詳解
關於不變模式的詳解: Java多執行緒程式設計中不變模式的詳解
關於生產者-消費者模式的詳解:生產者-消費者模式Java詳解
1. Future模式核心思想
Future模式的核心在於:去除了主函式的等待時間,並使得原本需要等待的時間段可以用於處理其他業務邏輯(根據《Java程式效能優化》)。
Future模式有點類似於商品訂單。在網上購物時,提交訂單後,在收貨的這段時間裡無需一直在家裡等候,可以先幹別的事情。類推到程式設計中時,當提交請求時,期望得到答覆時,如果這個答覆可能很慢。傳統的時一直等待到這個答覆收到時再去做別的事情,但如果利用Future設計模式就無需等待答覆的到來,在等待答覆的過程中可以幹其他事情。
例如如下的請求呼叫過程時序圖。當call請求發出時,需要很長的時間才能返回。左邊的圖需要一直等待,等返回資料後才能繼續其他操作;而右邊的Future模式的圖中客戶端則無需等到可以做其他的事情。伺服器段接收到請求後立即返回結果給客戶端,這個結果並不是真實的結果(是虛擬的結果),也就是先獲得一個假資料,然後執行其他操作。
2. Future模式Java實現
Client的實現
Client主要完成的功能包括:1. 返回一個FutureData;2.開啟一個執行緒用於構造RealData。
Data的實現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 } }
無論是FutureData還是RealData都實現該介面。
public interface Data {
String getResult() throws InterruptedException;
}
FutureData的實現
FutureData是Future模式的關鍵,它實際上是真實資料RealData的代理,封裝了獲取RealData的等待過程。
//FutureData是Future模式的關鍵,它實際上是真實資料RealData的代理,封裝了獲取RealData的等待過程
public class FutureData implements Data {
RealData realData = null; //FutureData是RealData的封裝
boolean isReady = false; //是否已經準備好
public synchronized void setRealData(RealData realData) {
if(isReady)
return;
this.realData = realData;
isReady = true;
notifyAll(); //RealData已經被注入到FutureData中了,通知getResult()方法
}
@Override
public synchronized String getResult() throws InterruptedException {
if(!isReady) {
wait(); //一直等到RealData注入到FutureData中
}
return realData.getResult();
}
}
RealData的實現
RealData是最終需要使用的資料,它的建構函式很慢。
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;
}
}
測試執行
主函式主要負責呼叫Client發起請求,並使用返回的資料。
public class Application {
public static void main(String[] args) throws InterruptedException {
Client client = new Client();
//這裡會立即返回,因為獲取的是FutureData,而非RealData
Data data = client.request("name");
//這裡可以用一個sleep代替對其他業務邏輯的處理
//在處理這些業務邏輯過程中,RealData也正在建立,從而充分了利用等待時間
Thread.sleep(2000);
//使用真實資料
System.out.println("資料="+data.getResult());
}
}
3. Future模式的JDK內建實現
由於Future是非常常用的多執行緒設計模式,因此在JDK中內建了Future模式的實現。這些類在java.util.concurrent包裡面。其中最為重要的是FutureTask類,它實現了Runnable介面,作為單獨的執行緒執行。在其run()方法中,通過Sync內部類呼叫Callable介面,並維護Callable介面的返回物件。當使用FutureTask.get()方法時,將返回Callable介面的返回物件。同樣,針對上述的例項,如果使用JDK自帶的實現,則需要作如下調整。
首先,Data介面和FutureData就不需要了,JDK幫我們實現了。
其次,RealData改為這樣:
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方法來表示真是業務是非常緩慢的
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return data;
}
}
最後,在測試執行時,這樣呼叫:
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); //使用執行緒池
//執行FutureTask,相當於上例中的client.request("name")傳送請求
executor.submit(futureTask);
//這裡可以用一個sleep代替對其他業務邏輯的處理
//在處理這些業務邏輯過程中,RealData也正在建立,從而充分了利用等待時間
Thread.sleep(2000);
//使用真實資料
//如果call()沒有執行完成依然會等待
System.out.println("資料=" + futureTask.get());
}
}
本文完。轉載請註明出處。
參考文獻
葛一鳴,Java程式效能優化.清華大學出版社.