知道線程池,那你知道Future類嗎?

內心豐富
拜託生活表面的相似

Future和Callable

callable的缺陷

  • 沒有返回值
    在這裏插入圖片描述

  • 不能拋出checked Exception

在這裏插入圖片描述
原因?

這些方法都不是我們開發人員自己編寫的,就算可以往外面拋,也只能報異常,不能按照我們想要的規則進行處理。

Callable接口

  1. 類似於Runnable,被其他線程執行的任務
  2. 實現call方法
  3. 有返回值
    在這裏插入圖片描述

Future類

Future是一個存儲類,它存儲call()這個任務的結果,而這個任務的執行時間是無法提前去確定的,因爲這完全取決於call()方法的執行。

我們可以通過Future.get來獲取Callable接口返回的執行結果,還可以通過Future.isDone()來判斷任務是否已經執行完畢,以及取消這個任務,限時獲取任務的結果等。

在call()未執行完畢之前,調用get()的線程(假定此時是主線程)會被阻塞知道call()方法返回結果後,此時future.get()纔會得到該結果,然後主線程纔會切換到Runnable狀態。

主要方法及具體實現

主要方法

可以看到 Future是一個接口,主要方法有5個。
在這裏插入圖片描述

  • get()方法

get()方法的行爲取決於Callable任務的狀態,有5種情況。

  1. 任務正常完成—get()方法會立即返回結果。
  2. 任務尚未完成(還沒開始或者進行中)—get()將阻塞並直到任務完成。
  3. 任務執行過程中拋出異常Exception
    get方法會拋出ExecutionException:這裏的拋出異常,是call()執行時產生的那個異常。不論call()執行是拋出的異常類型是什麼,最後get()拋出的異常類型都是ExecutionException。
  4. 任務被取消—get()方法會拋出CancellationException.
  5. 任務超時—get()有一個重載方法,是傳入一個延遲時間的。如果時間到了還沒有獲取到結果,get()方法就會拋出TimeoutException。
  • get(long timeout,TimuUnit unit):有超時的獲取。

用get(long timeout,TimuUnit unit)方法時,如果call()在規定的時間內完成了任務,那麼就會正常獲取到返回值;而如果再指定時間內沒有計算出結果,那麼就會拋出TimeoutException。

超時不獲取,任務需取消。

  • cancel方法

取消任務的執行。

  • isDone方法

判斷線程是否執行完畢

記住,是執行完畢,而不是執行成功,就算拋出異常,他也是執行完畢。

  • isCancelled方法

判斷是否被取消

具體實現

  • 線程池的submit方法返回Future對象

首先,我們要給線程池提交我們的任務,提交時線程池會立即返回給我們一個空的Future容器。當線程的任務一旦執行完畢,也就是當我們可以獲取結果的時候,線程池便會把該結果填入到之前給我們的那個Future中去(而不是創建一個新的Future),我們此時便可以從該Future中獲得任務執行的結果。
在這裏插入圖片描述

public class FutureDemo {
    public static void main(String[] args) throws Exception {
    	//創建線程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 5, TimeUnit.SECONDS, new LinkedBlockingQueue(10));
        //獲取Future對象,使用Lambl打印一個隨機值。
        Future<Integer> future = threadPoolExecutor.submit(()-> new Random().nextInt());
        try {
        	//前面說過,get()方法返回值可能會拋出異常。
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

在這裏插入圖片描述

  • 用FutureTask返回Future對象
    **

把Callable實例當做參數,生成FutureTask的對象,然後把這個對象當做一個Runnable對象,用線程池或另起線程去執行這個Runnable對象,最後通過FutureTask獲取剛纔執行的結果。

public class FutureDemo {
    public static void main(String[] args) {
        FutureTask<Integer> future2 = new FutureTask<>(()-> new Random().nextInt());
        new Thread(future2).start();
        try {
            System.out.println(future2.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //不起作用。複用
        new Thread(future2).start();
        try {
            System.out.println(future2.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

代碼中執行了2次
new Thread(future2).start();
得出的2條結果都是一樣的。
這就是達到了複用
在這裏插入圖片描述

注意點

  • 當for循環批量獲取Future的結果時,容易發生一部分線程很慢的情況(如果第一個線程計算很慢,後面的線程都計算結束了,那將會造成整個獲取結果很慢或阻塞),get()方法調用時應使用Timeout限制。
List<Future> futures = new ArrayList<>();
  • Future的生命週期不能後退

生命週期只能前進,不能後退。就和線程池的生命週期一樣,一旦完全完成了任務,他就永遠停在了“已完成”的狀態,不能重頭再來。

上面的代碼複用就是這種是思想。

文章持續更新,可以微信搜索「 紳堂Style 」第一時間閱讀,回覆【資料】有我準備的面試題筆記。
GitHub https://github.com/dtt11111/Nodes 有總結面試完整考點、資料以及我的系列文章。歡迎Star。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章