FutureTask 深度解析

先看下FutureTask的註釋吧

FutureTask一個可取消的異步計算,FutureTask 實現了Future的基本方法,提空 start cancel 操作,可以查詢計算是否已經完成,

並且可以獲取計算的結果。結果只可以在計算完成之後獲取,get方法會阻塞當計算沒有完成的時候,一旦計算已經完成,

那麼計算就不能再次啓動或是取消。

一個FutureTask 可以用來包裝一個 Callable 或是一個runnable對象。因爲FurtureTask實現了Runnable方法,所以一個 
FutureTask可以提交(submit)給一個Excutor執行(excution).


FutureTask 有兩個很重要的屬性 分別是 state  runner ,futureTask之所以可以支持cancel操作 就是因爲這兩個屬性
其中 state爲 枚舉值:
NEW                            新建            0
COMPLETING             執行中        1
NORMAL                    正常            2
EXCEPTIONAL            異常            3
CANCELLED                取消            4
INTERRUPTING        中斷中        5
INTERRUNPED            被中斷        6
state的狀態變化可以有四種方式
NEW->COMPLETING->NORMAL                            正常完成的流程
NEW->COMPLETING->EXCEPTIONAL                出現異常的流程
NEW->CANCELED                                                  被取消
NEW->INTERRUNPING->INTERRRUNPTED        被中斷

我們研究下Task的狀態變化也就是一個任務的生命週期:

我們創建一個FutureTask 首先會調用構造方法:

[java] view plaincopy
  1. public FutureTask(Runnable runnable, V result) {  
  2.         this.callable = Executors.callable(runnable, result);  
  3.         this.state = NEW;       // ensure visibility of callable  
  4.  }  

在我們構造Task的時候會把狀態 設置成 NEW 也就是所有 狀態變化路徑的起始狀態

我們創建完一個Task 會提交給Executes來執行(當然我們也可以自己啓動Thread來執行 效果基本是一樣,只是交給線程池執行Task可能會延遲執行)。

在之後的Task生命週期的變化 主要取決於 run()方法先被調用還是cancel ()方法會被調用,這兩個方法的執行順序決定了Task的生命週期的四種走向

我們先分析run方法先被調用的情況,爲了能對run()方法能更加詳細的理解我在run方法中加了增加了些註釋

[java] view plaincopy
  1. public void run() {  
  2.         /** 
  3.         *首先判斷任務的狀態 如果任務的狀態不是new 說明任務的狀態已經改變(說明他已經走了4種可能變化的一種) 
  4.         * 如果狀態是new就會把 當前執行任務的線程付給runner, 這裏用的cmpandset如果runner不爲空 說明已經有線程在執行 
  5.     * 任務也會退出執行,如果狀態是new並且runner爲空並且把當前的線程付給了runner那麼就繼續執行任務(runner state 都是 volatile 
  6.         *類型的變量是一個很輕量機的線程安全操作) 
  7.         *引起state狀態變化的原因 就是調用了cancel 或是 run 
  8.         **/  
  9.         if (state != NEW ||  
  10.             !UNSAFE.compareAndSwapObject(this, runnerOffset,  
  11.                                          null, Thread.currentThread()))  
  12.             return;  
  13.           
  14.         //開始執行任務  
  15.         try {  
  16.             Callable<V> c = callable;  
  17.             /** 
  18.             * 如果 要執行的任務不爲空 並且狀態 new 就執行 
  19.     
  20.             ***/  
  21.             if (c != null && state == NEW) {  
  22.                 V result;  
  23.                 boolean ran;  
  24.                 try {  
  25.                     //執行任務  
  26.                     result = c.call();  
  27.                     //如果沒有意外發生就執行成功了  
  28.                     ran = true;  
  29.                 } catch (Throwable ex) {  
  30.                         //有異常  
  31.                     result = null;  
  32.                     ran = false;  
  33.                     //設置異常  
  34.                     setException(ex);  
  35.                 }  
  36.                 //如果轉型成功了 設置結果  
  37.                 if (ran)  
  38.                     (result);  
  39.             }  
  40.         } finally {  
  41.             // runner must be non-null until state is settled to  
  42.             // prevent concurrent calls to run()  
  43.             //不管是否執行成功了 都把runner設置成null  
  44.             runner = null;  
  45.             // state must be re-read after nulling runner to prevent  
  46.             // leaked interrupts  
  47.             int s = state;  
  48.             if (s >= INTERRUPTING)  
  49.                 handlePossibleCancellationInterrupt(s);  
  50.         }  
  51.     }  
 Task執行後如果成功會調用set()方法,如果有異常會調用setException()方法。

我們先看下set方法 :

[java] view plaincopy
  1. protected void set(V v) {  
  2.         /** 
  3.         *如過state是new 把state設置成 COMPLETING 
  4.         * 
  5.         **/  
  6.           
  7.       if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {  
  8.           outcome = v;  
  9.           //將任務設置成NORMAL   over the task  
  10.           UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state  
  11.           finishCompletion();  
  12.       }  
  13.   }  

如果現在的狀態是NEW 就把狀態設置成COMPLETING 然後設置成NORMAL。 這個執行流程導致的狀態變化就是

NEW->COMPLETING->NORMAL    

執行步驟是 首先執行 run() 並且Task正常完成而且在這其間沒有調用cancel()

上邊是任務正常執行完成的狀態變化,我們在看下有異常的情況。有異常的話會調用setException()方法:

[java] view plaincopy
  1. protected void setException(Throwable t) {  
  2.      /** 
  3.             *如過state是new 把state設置成 COMPLETING 
  4.             * 
  5.             **/  
  6.         if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {  
  7.             outcome = t;  
  8.              //將任務設置成EXCEPTIONAL   
  9.             UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state  
  10.             finishCompletion();  
  11.         }  
  12.     }  

如果現在的狀態是NEW 就把狀態設置成COMPLETING 然後設置成EXCEPTIONAL。 這個執行流程導致的狀態變化就是

NEW->COMPLETING->EXCEPTIONAL  
執行步驟是 首先執行 run() 並且Task拋出異常而且在這其間沒有調用cancel()。

上文所分析的場景只是run()方法被調用了而在run()方法執行的過程中 調用cancel()並沒有分析,兩個方法有時間交集的情況我們稍後分析。

現在我們分析下cancel()方法先被調用的情況

上cancel()代碼吧

[java] view plaincopy
  1. /這個方法有一個參數 是否中斷running  
  2.      public boolean cancel(boolean mayInterruptIfRunning) {  
  3.           /** 
  4.           * 這個有點暈啊邏輯關係是 
  5.           * 等價與 if(state!=new || !UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)) 
  6.           * 這個意思是 如果state不是new 那麼就退出方法,這時的任務任務坑是已經完成了 或是被取消了 或是被中斷了 
  7.           * 如果是state 是new 就設置state 爲中斷狀態 或是取消狀態 
  8.           * 
  9.           **/  
  10.         if (!(state == NEW &&  
  11.               UNSAFE.compareAndSwapInt(this, stateOffset, NEW,  
  12.                   mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))  
  13.             return false;  
  14.         try {    // in case call to interrupt throws exception  
  15.             //如果是可中斷 那麼就 調用系統中斷方法 然後把狀態設置成INTERRUPTED  
  16.             if (mayInterruptIfRunning) {  
  17.                 try {  
  18.                     Thread t = runner;  
  19.                     if (t != null)  
  20.                         t.interrupt();  
  21.                 } finally { // final state  
  22.                     UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);  
  23.                 }  
  24.             }  
  25.         } finally {  
  26.             finishCompletion();  
  27.         }  
  28.         return true;  
  29.     }  

這個方法很簡單 :

1.如果是cancel(false) 那麼Task的狀態變化就是

 NEW->=CANCELLED

2.如果是cancel(true)那麼Task的狀態化就是

 NEW->INTERRUPTING ->INTERRUPTED

至此Task的四種狀態變化我們都看到了,不過這都是在兩個方法都是單獨執行的情況。

我們在分析下兩個方法交叉執行的情況( run()->cancel() ):

1.如果Task已經執行然後再調用cancel():

     A:調用cancel(false)情況
           a:如果Task已經在執行而callable.call()沒有返回 或是 call()已經返回但是state狀態還沒有改變
              那麼任務調用cancel(false) 不會對任務的執行造成影響 只會影響task的狀態 
           

           b:如果callable.call()已經返回並且狀態已經變成COMPLETING或是 COMPLED 那麼對任務執行 和任務狀態都沒有影響
    
    B:調用cancel(true)

          a:如果任務已經在執行而callable.call()沒有返回 會把state設置成 INTERRUPTING然後調用執行線程的中斷請求 然後把狀態設置成INTERRUPTED,這裏 如果
                callable.call()方法可以響應中斷 可能對任務執行產生影響,如果方法不會響應中斷不會對任務運行產生影響。影響任務的狀態
      
          b:.如果任務已經在執行並且 call()已經返回但是state狀態還沒有改變  不會對任務的執行造成影響 只會影響任務的狀態 。

2。調用cancel()後 在執行任務 ( cancel() -> run() )

      先調用cancel()無論是那種調用方式都會引起state狀態的變化。在run()方法執行的時候發現state已經不是new了 就會放棄任務的執行



發佈了6 篇原創文章 · 獲贊 5 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章