首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

FutureTask 源碼深度解析:狀態機、執行流程、取消機制與 get() 阻塞原理

     編輯:Java 綜合教學

FutureTask 源碼深度解析:狀態機、執行流程、取消機制與 get() 阻塞原理

FutureTask 是 JDK 1.7 前 Future 唯一實現,JDK 8 仍廣泛用於線程池任務提交。本文基於源碼剖析其 7 狀態機、run() 執行流程、get() 阻塞喚醒、cancel() 取消語義及爭議行為。

Future 接口核心方法

  • cancel(boolean mayInterruptIfRunning):嘗試取消任務
  • isCancelled() / isDone():狀態查詢
  • get() / get(timeout, unit):阻塞獲取結果或異常

FutureTask 繼承結構

FutureTask implements RunnableFuture 
RunnableFuture extends Runnable, Future

即可作為 Runnable 提交執行,也可作為 Future 獲取結果。

七大內部狀態

狀態常量值含義
NEW0初始狀態,構造函數設置
COMPLETING1任務即將完成(正常或異常)
NORMAL2正常完成,結果保存在 outcome
EXCEPTIONAL3異常完成,異常對象保存在 outcome
CANCELLED4取消完成(mayInterruptIfRunning=false)
INTERRUPTING5正在中斷執行線程
INTERRUPTED6中斷完成(mayInterruptIfRunning=true)

狀態流轉路徑:

  1. 正常完成:NEW → COMPLETING → NORMAL
  2. 異常完成:NEW → COMPLETING → EXCEPTIONAL
  3. 取消(不中斷):NEW → CANCELLED
  4. 取消(中斷):NEW → INTERRUPTING → INTERRUPTED

run() 執行流程源碼解讀

public void run() {
    if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
        return; // 狀態非 NEW 或 runner 已被設置,直接返回

    try {
        Callable c = callable;
        if (c != null && state == NEW) {
            V result; boolean ran = false;
            try {
                result = c.call(); // 執行用戶任務
                ran = true;
            } catch (Throwable ex) {
                result = null;
                setException(ex); // 異常路徑:state → COMPLETING → EXCEPTIONAL
            }
            if (ran) set(result); // 正常路徑:state → COMPLETING → NORMAL
        }
    } finally {
        runner = null; // 清除 runner 引用
        int s = state;
        if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); // 處理取消中斷殘留
    }
}

關鍵點:

  • CAS 設置 runner 確保單次執行
  • set(V)/setException(Throwable) 內部 CAS 更新狀態,寫入 outcome,最後 finishCompletion() 喚醒等待線程
  • outcome 字段雙重用途:正常時存結果,異常時存異常對象

get() 阻塞與喚醒機制

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING) s = awaitDone(false, 0L); // 狀態未終態,進入阻塞等待
    return report(s);
}
private V report(int s) throws ExecutionException {
    Object x = outcome;
    if (s == NORMAL) return (V) x;
    if (s >= CANCELLED) throw new CancellationException();
    throw new ExecutionException((Throwable) x);
}

awaitDone 內部使用 LockSupport.park/parkNanos 掛起線程,任務完成時 finishCompletion() 調用 LockSupport.unpark 喚醒。超時版本 get(timeout, unit) 同理,超時拋 TimeoutException。

cancel(boolean) 爭議行為深度分析

public boolean cancel(boolean mayInterruptIfRunning) {
    if (state != NEW) return false; // 非 NEW 狀態不可取消
    if (mayInterruptIfRunning) {
        if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING)) return false;
        Thread t = runner;
        if (t != null) t.interrupt(); // 僅設置中斷標誌!
        UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
    } else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED)) return false;
    finishCompletion();
    return true;
}

實測現象

提交耗時 1s 的任務,立即調用 cancel(false),兩種輸出:

  1. 任務未啟動:cancel 返回 true,任務從未運行
  2. 任務已啟動:cancel 返回 true,但任務繼續跑完(打印 "當前任務執行已經超過1s")

原因:cancel(false) 僅修改狀態為 CANCELLED,不中斷執行線程。線程繼續運行直到正常返回或異常,但 FutureTask 狀態已終態,後續 get() 拋 CancellationException。

cancel(true) 行為

調用 runner.interrupt() 設置中斷標誌。僅當任務代碼響應中斷(拋 InterruptedException、檢查 interrupted())時才能真正停止。若任務為死循環 CPU 計算,中斷無效。

總結

  • FutureTask 狀態機嚴格控制任務生命周期,CAS 保證線程安全
  • get() 通過 LockSupport 高效阻塞/喚醒
  • cancel() 不保證任務停止:false 僅改狀態,true 僅發中斷信號
  • 任務代碼必須協作式響應中斷(檢查 Thread.interrupted()、拋 InterruptedException)
  • 理解源碼有助於正確使用 Future、排查任務「取消不生效」、異常吞沒等問題
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved