Future
Future模式是多線程開發中非常常見的一種設計模式,它的核心思想是異步調用。當我們需要調用一個函數方法時,如果這個函數執行很慢,那麼我們就要進行等待。但有時候,我們可能不急着要結果。因此,我們可以讓被調者立即返回,讓它在後臺慢慢處理這個請求。對於調用者來說,則可以先處理一些其他任務,在真正需要數據的場合再去嘗試獲得需要的數據。
對於Future模式來說,雖然它無法立即給出你需要的數據。但是,它會返回給你一個契約,將來,你可以憑藉這個契約去重新獲取你需要的信息。
Future的簡易實現
參與者 | 作用 |
---|---|
Main | 系統啓動,調用Client發出請求 |
Client | 返回Data對象,立即返回FutureData,並開啓ClientThread線程裝配RealData |
Data | 返回數據的接口 |
FutureData | Future數據,構造很快,但是是一個虛擬的過程,需要裝配RealData |
RealData | 真實數據,其構造是比較慢的 |
下面是data接口
public interface Data {
public String getResult();
}
FutureData:
public class FutureData implements Data{
protected RealData realData = null;
protected boolean isReady = false;
public synchronized void setRealData(RealData realData){
if(isReady){
return;
}
this.realData = realData;
isReady = true;
notifyAll();
}
@Override
public synchronized String getResult() {
while (!isReady){
try{
wait();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
return realData.result;
}
}
RealData
public class RealData implements Data{
protected final String result;
public RealData(String para){
//RealData的構造可能很慢,需要用戶等待很久,這裏用seelp模擬
StringBuffer sb = new StringBuffer();
for(int i = 0; i < 10; i++){
sb.append(para);
}
try{
//這裏用sleep,代替一個很慢的操作過程
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
result = sb.toString();
}
@Override
public String getResult() {
return result;
}
}
Client類:
public class Client {
public Data request(final String queryStr){
final FutureData future = new FutureData();
new Thread(){
public void run(){ //RealData的構建很慢,所以在單獨線程中進行
RealData realData = new RealData(queryStr);
future.setRealData(realData);
}
}.start();
return future;
}
}
Main函數
public class Main {
public static void main(String[] args){
Client client = new Client();
//這裏會立即返回,因爲得到的是FutureData而不是RealData
Data data= client.request("name");
System.out.println("請求完畢");
try{
//這裏可以用一個sleep代替了對其他業務邏輯的處理
//在處理這些業務邏輯的過程中,RealData被創建,從而充分利用了等待時間
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("數據= " + data.getResult());
}
}
JDK中的Future模式
可以通過Future接口來得到真實的數據。RunnableFuture繼承了Future和Ruuable接口,其中run()方法用於構造真實的數據。它有一個具體的實現FutureTask類。FutureTask有一個內部類Sync,一些實質性的工作,會委託Sync類實現。而Sync類最終會調用Callable接口,完成實際數據的組裝工作。
具體使用
RealData
package JDKFuture;
import java.util.concurrent.Callable;
public class RealData implements Callable<String> {
private String para;
public RealData(String para){
this.para = para;
}
@Override
public String call() throws Exception{
StringBuffer sb = new StringBuffer();
for(int i = 0 ; i < 10; i++){
sb.append(para);
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
return sb.toString();
}
}
FutureMain
package JDKFuture;
import org.omg.Messaging.SYNC_WITH_TRANSPORT;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
public class FutureMain {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//構造FutureTask
FutureTask<String> future = new FutureTask<String>(new RealData("a"));
ExecutorService executor = Executors.newFixedThreadPool(1);
//執行FutureTask,相當與上例中的client.request("a")發送請求
//在這裏開啓線程進行RealData的call()執行
executor.submit(future);
System.out.println("請求完畢");
try{
//這裏依然可以做額外操作,這裏使用sleep代替其他業務邏輯的處理
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
//相當於data.getResult(),取得call()方法的返回值
//如果此時call()方法沒有執行完成,則依然會等待
System.out.println("數據 = " + future.get());
}
}
除了基本功能wait,JDK還爲Future接口提供了一些簡單的控制功能。
boolean cancel(boolean mayInterruptIfRunning); //取消任務
boolean isCancelled(); //是否已經取消
boolean isDone(); //是否已完成
V get() throws InterruptedException,ExecutionException; //取得返回對象
V get(long timeout,TimeUnit unit) //取得返回對象,可以設置超時時間