JAVA Future類詳解
1. Future的應用場景
在並發編程中,我們經常用到非阻塞的模型,在之前的多線程的三種實現中,不管是繼承thread類還是實現runnable接口,都無法保證獲取到之前的執行結果。通過實現Callback接口,並用Future可以來接收多線程的執行結果。
Future表示一個可能還沒有完成的異步任務的結果,針對這個結果可以添加Callback以便在任務執行成功或失敗後作出相應的操作。
舉個例子:比如去吃早點時,點了包子和涼菜,包子需要等3分鐘,涼菜只需1分鐘,如果是串行的一個執行,在吃上早點的時候需要等待4分鐘,但是因為你在等包子的時候,可以同時準備涼菜,所以在準備涼菜的過程中,可以同時準備包子,這樣只需要等待3分鐘。那Future這種模式就是後面這種執行模式。
2. Future的類圖結構
Future接口定義了主要的5個接口方法,有RunnableFuture和SchedualFuture繼承這個接口,以及CompleteFuture和ForkJoinTask繼承這個接口。
RunnableFuture
這個接口同時繼承Future接口和Runnable接口,在成功執行run()方法後,可以通過Future訪問執行結果。這個接口都實現類是FutureTask,一個可取消的異步計算,這個類提供了Future的基本實現,後面我們的demo也是用這個類實現,它實現了啟動和取消一個計算,查詢這個計算是否已完成,恢復計算結果。計算的結果只能在計算已經完成的情況下恢復。如果計算沒有完成,get方法會阻塞,一旦計算完成,這個計算將不能被重啟和取消,除非調用runAndReset方法。
FutureTask能用來包裝一個Callable或Runnable對象,因為它實現了Runnable接口,而且它能被傳遞到Executor進行執行。為了提供單例類,這個類在創建自定義的工作類時提供了protected構造函數。
SchedualFuture
這個接口表示一個延時的行為可以被取消。通常一個安排好的future是定時任務SchedualedExecutorService的結果
CompleteFuture
一個Future類是顯示的完成,而且能被用作一個完成等級,通過它的完成觸發支持的依賴函數和行為。當兩個或多個線程要執行完成或取消操作時,只有一個能夠成功。
ForkJoinTask
基於任務的抽象類,可以通過ForkJoinPool來執行。一個ForkJoinTask是類似於線程實體,但是相對於線程實體是輕量級的。大量的任務和子任務會被ForkJoinPool池中的真實線程掛起來,以某些使用限制為代價。
3. Future的主要方法
Future接口主要包括5個方法
get()方法可以當任務結束後返回一個結果,如果調用時,工作還沒有結束,則會阻塞線程,直到任務執行完畢
get(long timeout,TimeUnit unit)做多等待timeout的時間就會返回結果
cancel(boolean mayInterruptIfRunning)方法可以用來停止一個任務,如果任務可以停止(通過mayInterruptIfRunning來進行判斷),則可以返回true,如果任務已經完成或者已經停止,或者這個任務無法停止,則會返回false.
isDone()方法判斷當前方法是否完成
isCancel()方法判斷當前方法是否取消
4. Future示例demo
需求場景:等早餐過程中,包子需要3秒,涼菜需要1秒,普通的多線程需要四秒才能完成。先等涼菜,再等包子,因為等涼菜時,普通多線程啟動start()方法,執行run()中具體方法時,沒有返回結果,所以如果要等有返回結果,必須是要1秒結束後才知道結果。
普通多線程:
public class BumThread extends Thread{ @Override public void run() { try { Thread.sleep(1000*3); System.out.println("包子準備完畢"); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class ColdDishThread extends Thread{ @Override public void run() { try { Thread.sleep(1000); System.out.println("涼菜準備完畢"); } catch (InterruptedException e) { e.printStackTrace(); } } }
public static void main(String[] args) throws InterruptedException { long start = System.currentTimeMillis(); // 等涼菜 -- 必須要等待返回的結果,所以要調用join方法 Thread t1 = new ColdDishThread(); t1.start(); t1.join(); // 等包子 -- 必須要等待返回的結果,所以要調用join方法 Thread t2 = new BumThread(); t2.start(); t2.join(); long end = System.currentTimeMillis(); System.out.println("準備完畢時間:"+(end-start)); }
采用Future模式:
public static void main(String[] args) throws InterruptedException, ExecutionException { long start = System.currentTimeMillis(); // 等涼菜 Callable ca1 = new Callable(){ @Override public String call() throws Exception { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "涼菜準備完畢"; } }; FutureTask<String> ft1 = new FutureTask<String>(ca1); new Thread(ft1).start(); // 等包子 -- 必須要等待返回的結果,所以要調用join方法 Callable ca2 = new Callable(){ @Override public Object call() throws Exception { try { Thread.sleep(1000*3); } catch (InterruptedException e) { e.printStackTrace(); } return "包子準備完畢"; } }; FutureTask<String> ft2 = new FutureTask<String>(ca2); new Thread(ft2).start(); System.out.println(ft1.get()); System.out.println(ft2.get()); long end = System.currentTimeMillis(); System.out.println("準備完畢時間:"+(end-start)); }
詳解Java中的Future、FutureTask的原理以及與線程池的搭配使用 https://blog.csdn.net/wei_lei/article/details/74262818
JAVA Future類詳解