深入理解[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<String> {
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<String> futureTask = new FutureTask<String>(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模式的核心在於去除了主函式中的等待時間,並使得原來需要等待的時間段可以用於處理其他的業務邏輯,從而充分利用計算機資源。