先看下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 首先會調用構造方法:
- public FutureTask(Runnable runnable, V result) {
- this.callable = Executors.callable(runnable, result);
- this.state = NEW; // ensure visibility of callable
- }
在我們構造Task的時候會把狀態 設置成 NEW 也就是所有 狀態變化路徑的起始狀態
我們創建完一個Task 會提交給Executes來執行(當然我們也可以自己啓動Thread來執行 效果基本是一樣,只是交給線程池執行Task可能會延遲執行)。
在之後的Task生命週期的變化 主要取決於 run()方法先被調用還是cancel ()方法會被調用,這兩個方法的執行順序決定了Task的生命週期的四種走向
我們先分析run方法先被調用的情況,爲了能對run()方法能更加詳細的理解我在run方法中加了增加了些註釋
- public void run() {
- /**
- *首先判斷任務的狀態 如果任務的狀態不是new 說明任務的狀態已經改變(說明他已經走了4種可能變化的一種)
- * 如果狀態是new就會把 當前執行任務的線程付給runner, 這裏用的cmpandset如果runner不爲空 說明已經有線程在執行
- * 任務也會退出執行,如果狀態是new並且runner爲空並且把當前的線程付給了runner那麼就繼續執行任務(runner state 都是 volatile
- *類型的變量是一個很輕量機的線程安全操作)
- *引起state狀態變化的原因 就是調用了cancel 或是 run
- **/
- if (state != NEW ||
- !UNSAFE.compareAndSwapObject(this, runnerOffset,
- null, Thread.currentThread()))
- return;
- //開始執行任務
- try {
- Callable<V> c = callable;
- /**
- * 如果 要執行的任務不爲空 並且狀態 new 就執行
- ***/
- if (c != null && state == NEW) {
- V result;
- boolean ran;
- try {
- //執行任務
- result = c.call();
- //如果沒有意外發生就執行成功了
- ran = true;
- } catch (Throwable ex) {
- //有異常
- result = null;
- ran = false;
- //設置異常
- setException(ex);
- }
- //如果轉型成功了 設置結果
- if (ran)
- (result);
- }
- } finally {
- // runner must be non-null until state is settled to
- // prevent concurrent calls to run()
- //不管是否執行成功了 都把runner設置成null
- runner = null;
- // state must be re-read after nulling runner to prevent
- // leaked interrupts
- int s = state;
- if (s >= INTERRUPTING)
- handlePossibleCancellationInterrupt(s);
- }
- }
我們先看下set方法 :
- protected void set(V v) {
- /**
- *如過state是new 把state設置成 COMPLETING
- *
- **/
- if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
- outcome = v;
- //將任務設置成NORMAL over the task
- UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
- finishCompletion();
- }
- }
如果現在的狀態是NEW 就把狀態設置成COMPLETING 然後設置成NORMAL。 這個執行流程導致的狀態變化就是
NEW->COMPLETING->NORMAL
執行步驟是 首先執行 run() 並且Task正常完成而且在這其間沒有調用cancel()
上邊是任務正常執行完成的狀態變化,我們在看下有異常的情況。有異常的話會調用setException()方法:
- protected void setException(Throwable t) {
- /**
- *如過state是new 把state設置成 COMPLETING
- *
- **/
- if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
- outcome = t;
- //將任務設置成EXCEPTIONAL
- UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
- finishCompletion();
- }
- }
如果現在的狀態是NEW 就把狀態設置成COMPLETING 然後設置成EXCEPTIONAL。 這個執行流程導致的狀態變化就是
NEW->COMPLETING->EXCEPTIONAL
執行步驟是 首先執行 run() 並且Task拋出異常而且在這其間沒有調用cancel()。
上文所分析的場景只是run()方法被調用了而在run()方法執行的過程中 調用cancel()並沒有分析,兩個方法有時間交集的情況我們稍後分析。
現在我們分析下cancel()方法先被調用的情況
上cancel()代碼吧
- /這個方法有一個參數 是否中斷running
- public boolean cancel(boolean mayInterruptIfRunning) {
- /**
- * 這個有點暈啊邏輯關係是
- * 等價與 if(state!=new || !UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED))
- * 這個意思是 如果state不是new 那麼就退出方法,這時的任務任務坑是已經完成了 或是被取消了 或是被中斷了
- * 如果是state 是new 就設置state 爲中斷狀態 或是取消狀態
- *
- **/
- if (!(state == NEW &&
- UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
- mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
- return false;
- try { // in case call to interrupt throws exception
- //如果是可中斷 那麼就 調用系統中斷方法 然後把狀態設置成INTERRUPTED
- if (mayInterruptIfRunning) {
- try {
- Thread t = runner;
- if (t != null)
- t.interrupt();
- } finally { // final state
- UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
- }
- }
- } finally {
- finishCompletion();
- }
- return true;
- }
這個方法很簡單 :
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了 就會放棄任務的執行