老生常談java中的Future模式
jdk1.7.0_79
本文實際上是對上文《簡單談?wù)凾hreadPoolExecutor線程池之submit方法》的一個延續(xù)或者一個補充。在上文中提到的submit方法里出現(xiàn)了FutureTask,這不得不停止腳步將方向轉(zhuǎn)向Java的Future模式。
Future是并發(fā)編程中的一種設(shè)計模式,對于多線程來說,線程A需要等待線程B的結(jié)果,它沒必要一直等待B,可以先拿到一個未來的Future,等B有了結(jié)果后再取真實的結(jié)果。
ExecutorService executor = Executors.newSingleThreadExecutor(); Future<String> future = executor.submit(callable); //主線程需要callable線程的結(jié)果,先拿到一個未來的Future System.out.println(future.get()); //有了結(jié)果后再根據(jù)get方法取真實的結(jié)果,當然如果此時callable線程如果沒有執(zhí)行完get方法會阻塞執(zhí)行完,如果執(zhí)行完則直接返回結(jié)果或拋出異常
也就是說,Future它代表一個異步計算的結(jié)果。

上面就代表了Future模式的執(zhí)行原理,根據(jù)網(wǎng)上的例子,我們可以來自己實現(xiàn)一個Future模式。
package com.future;
/**
* 數(shù)據(jù)結(jié)果
* Created by yulinfeng on 6/18/17.
*/
public interface Data {
String getResult() throws InterruptedException;
}
package com.future;
/**
* 結(jié)果的真實計算過程
* Created by yulinfeng on 6/18/17.
*/
public class RealData implements Data {
protected String data;
public RealData(String data) {
try {
System.out.println("正在計算結(jié)果");
Thread.sleep(3000); //模擬計算
} catch (InterruptedException e) {
e.printStackTrace();
}
this.data = data + “ world”;
}
public String getResult() throws InterruptedException {
return data;
}
}
package com.future;
/**
* 真實結(jié)果RealData的代理
* Created by yulinfeng on 6/18/17.
*/
public class FutureData implements Data {
RealData realData = null; //對RealData的封裝,代理了RealData
boolean isReady = false; //真實結(jié)果是否已經(jīng)準備好
public synchronized void setResultData(RealData realData) {
if (isReady) {
return;
}
this.realData = realData;
isReady = true;
notifyAll(); //realData已經(jīng)被注入到了futureData中,通知getResult方法
}
public synchronized String getResult() throws InterruptedException {
if (!isReady) {
wait(); //數(shù)據(jù)還未計算好,阻塞等待
}
return realData.getResult();
}
}
package com.future;
/**
* Client主要完成的功能包括:1. 返回一個FutureData;2.開啟一個線程用于構(gòu)造RealData
* Created by yulinfeng on 6/18/17.
*/
public class Client {
public Data request(final String string) {
final FutureData futureData = new FutureData();
/*計算過程比較慢,單獨放到一個線程中去*/
new Thread(new Runnable() {
public void run() {
RealData realData = new RealData(string);
futureData.setResultData(realData);
}
}).start();
return futureData; //先返回一個“假”的futureData
}
}
/**
* 負責調(diào)用Client發(fā)起請求,并使用返回的數(shù)據(jù)。
* Created by yulinfeng on 6/18/17.
*/
public class Main {
public static void main(String[] args) throws InterruptedException {
Client client = new Client();
System.out.println("準備計算結(jié)果");
Data data = client.request("hello"); //立即返回一個“假”的futureData,可以不用阻塞的等待數(shù)據(jù)返回,轉(zhuǎn)而執(zhí)行其它任務(wù)
System.out.println("執(zhí)行其它任務(wù)");
Thread.sleep(3000); //模擬執(zhí)行其它任務(wù)
System.out.println("數(shù)據(jù)的計算結(jié)果為:" + data.getResult());
}
}
仔細閱讀以上程序?qū)uture模式的實現(xiàn)不難發(fā)現(xiàn),F(xiàn)uture模式是異步請求和代理模式的結(jié)合。當然在JDK中已經(jīng)為我們實現(xiàn)好了Future模式。
修改RealData類:
package com.future;
import java.util.concurrent.Callable;
/**
* 結(jié)果的真實計算過程
* Created by yulinfeng on 6/18/17.
*/
public class RealData2 implements Callable<String> {
protected String data;
public RealData2(String data) {
this.data = data;
}
public String call() throws Exception {
try {
System.out.println("正在計算結(jié)果");
Thread.sleep(2000); //模擬計算結(jié)果
} catch (InterruptedException e) {
e.printStackTrace();
}
this.data = data + " world";
return data;
}
}
修改Main測試類:
package com.future;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 負責調(diào)用Executor的submit,并使用返回的數(shù)據(jù)。
* Created by yulinfeng on 6/18/17.
*/
public class Main2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService client = Executors.newSingleThreadExecutor(); //類似Client
System.out.println("準備計算結(jié)果");
Future<String> data = client.submit(new RealData2("hello")); //類似Client.request
System.out.println("執(zhí)行其它任務(wù)");
Thread.sleep(3000);
System.out.println("數(shù)據(jù)的計算結(jié)果為:" + data.get());
}
}
現(xiàn)在回到上文還未解決完的AbstractExecutorService#submit方法。
類比上面的Client#request方法,在Client#request中先創(chuàng)建一個FutureData實例,而在AbstractExecutorService#submit中則是創(chuàng)建一個FutureTask實例,接著Client#request新創(chuàng)建一個線程用于異步執(zhí)行任務(wù),并直接返回FutureData,而在AbstractExecutorService#submit中同樣也將任務(wù)交給了execute方法,并直接返回FutureTask。當然JDK中Future模式的實現(xiàn)更為復雜。
在《ThreadPoolExecutor線程池原理及其execute方法》中我們講解了execute方法,在ThreadPoolExecutor$Worker#runWorker方法第1145行中是對task任務(wù)的調(diào)用:
//ThreadPoolExecutor$Worker#runWorker task.run();
submit調(diào)用execute以執(zhí)行run方法,實際執(zhí)行的是FutureTask中的run方法。在FutureTask#run中,可以看到對任務(wù)Callable類型的task異步的執(zhí)行,以及結(jié)果的保存。
以上這篇老生常談java中的Future模式就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java面向?qū)ο蟪绦蛟O(shè)計:繼承,多態(tài)用法實例分析
這篇文章主要介紹了Java面向?qū)ο蟪绦蛟O(shè)計:繼承,多態(tài)用法,結(jié)合實例形式分析了java繼承與多態(tài)的相關(guān)概念、原理、實現(xiàn)方法與操作注意事項,需要的朋友可以參考下2020-04-04
Java創(chuàng)建二叉搜索樹,實現(xiàn)搜索,插入,刪除的操作實例
下面小編就為大家分享一篇Java創(chuàng)建二叉搜索樹,實現(xiàn)搜索,插入,刪除的操作實例,具有很好的參考價值,希望對大家有所幫助2017-12-12
RestTemplate實現(xiàn)發(fā)送帶headers的GET請求
這篇文章主要介紹了RestTemplate實現(xiàn)發(fā)送帶headers的GET請求,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
JAVA實現(xiàn)空間索引編碼——GeoHash的示例
本篇文章主要介紹了JAVA實現(xiàn)空間索引編碼——GeoHash的示例,如何從眾多的位置信息中查找到離自己最近的位置,有興趣的朋友可以了解一下2016-10-10

