1. 程式人生 > >轉多線程設計模式 - Future模式之JAVA原生實現

轉多線程設計模式 - Future模式之JAVA原生實現

pre 復雜 業務邏輯 inter real 保存 如何 http adp

 在之前一篇博客中介紹了Future設計模式的設計思想以及具體實現,今天我們來講一下使用JDK原生的包如何實現。

  JDK內置的Future主要使用到了Callable接口和FutureTask類。

  Callable是類似於Runnable的接口,實現Callable接口的類和實現Runnable的類都是可被其他線程執行的任務。Callable接口的定義如下:

技術分享圖片
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
技術分享圖片

  Callable的類型參數是返回值的類型。例如:

Callable<Integer>表示一個最終返回Integer對象的異步計算。

  Future保存異步計算的結果。實際應用中可以啟動一個計算,將Future對象交給某個線程,然後執行其他操作。Future對象的所有者在結果計算好之後就可以獲得它。Future接口具有下面的方法:

技術分享圖片
public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
技術分享圖片

  第一個get方法的調用被阻塞,直到計算完成。如果在計算完成之前,第二個get方法的調用超時,拋出一個TimeoutException異常。如果運行該計算的線程被中斷,兩個方法都將拋出InterruptedException。如果計算已經完成,那麽get方法立即返回。

  如果計算還在進行,isDone方法返回false;如果完成了,則返回true。

  可以用cancel方法取消該計算。如果計算還沒有開始,它被取消且不再開始。如果計算處於運行之中,那麽如果mayInterrupt參數為true,它就被中斷。

  FutureTask包裝器是一種非常便利的機制,同時實現了Future和Runnable接口。FutureTask有2個構造方法:

技術分享圖片
public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}
技術分享圖片

  通常,我們會使用Callable示例構造一個FutureTask對象,並將它提交給線程池進行處理,下面我們將展示這個內置的Future模式的使用。

技術分享圖片
public class RealData implements Callable<String> {
    private String param;
    public RealData(String param){
        this.param = param;
    }
    @Override
    public String call() throws Exception {
        StringBuffer sb = new StringBuffer();
        for(int i = 0 ; i< 10 ;i++){
            sb.append(param);
            try {
                Thread.sleep(100);
            }catch (InterruptedException e){
                
            }
        }
        return sb.toString();
    }
}
技術分享圖片

  上述代碼實現了Callable接口,它的Call方法會構造我們需要的真實數據並返回,當然這個過程比較緩慢,這裏使用Thread.sleep()來模擬它:

技術分享圖片
public class FutureMain {
    public static void main(String[] args)
            throws ExecutionException, InterruptedException {
        //構造FutureTask
        FutureTask<String> futureTask = new FutureTask<String>(new RealData("xxx"));
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        //執行FutureTask,發送請求
        //在這裏開啟線程進行RealData的call()執行
        executorService.submit(futureTask);

        System.out.println("請求完畢。。。");
        try {
            //這裏可以進行其他額外的操作,這裏用sleep代替其他業務的處理
            Thread.sleep(200);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
        //獲取call()方法的返回值
        //如果此時call()方法沒有執行完成,則依然會等待
        System.out.println("真實數據:"+futureTask.get());
    }
}
技術分享圖片

  上述代碼就是使用Future模式的典型。構造FutureTask時使用Callable接口,告訴FutureTask我們需要的數據應該有返回值。然後將FutureTask提交給線程池,接下來我們不用關心數據是怎麽產生的,可以去做其他的業務邏輯處理,然後在需要的時候調用FutureTask.get()得到實際的數據。

  Future模式在日常業務中處理復雜業務時會經常用到,希望大家都能掌握。

轉多線程設計模式 - Future模式之JAVA原生實現