JDK 源碼分析 -- FutureTask

 

一、簡介

FutureTask 可取消的異步計算(主線程可以去做其他的。 此類提供Future的基本實現,其中包含啓動和取消計算,查詢以查看計算是否完成以及獲取計算結果的方法。 只有在計算完成後才能獲取結果; 如果計算尚未完成,則get方法將阻塞。 一旦計算完成,就不能重新啓動或取消計算(除非使用runAndReset調用計算)。
FutureTask可用於包裝Callable或Runnable對象。 由於FutureTask實現Runnable,因此FutureTask可以提交給執行程序以執行。
除了用作獨立類之外,此類還提供受保護的功能,這些功能在創建自定義任務類時可能會有用。

 

二、源碼閱讀 (基於 Java8)

Java 併發包核心要素, 狀態位stat,隊列,CAS,Java8的實現依然是這樣實現。

修訂說明:與本版本的先前版本 Java7 不同
依賴AbstractQueuedSynchronizer的類,主要用於
避免使用戶驚訝於在取消競爭的時候保留中斷狀態
。 當前設計中的同步控制依賴
通過CAS更新“狀態”字段以跟蹤完成情況,以及
一個簡單的Treiber堆棧來保存等待線程。

樣式說明:與往常一樣,我們繞過了使用的開銷
AtomicXFieldUpdaters,而是直接使用Unsafe內部函數。

 

FutureTask 實現了 Runnable 和 Future。

先看下靜態代碼塊,CAS操作主要針對3個屬性,包括state、runner和waiters,說明這3個屬性基本是會被多個線程同時訪問的。其中state屬性代表了任務的狀態,waiters屬性代表了指向棧頂節點的指針,runner屬性代表了執行FutureTask中的“Task”的線程:

// Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long stateOffset;
    private static final long runnerOffset;
    private static final long waitersOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> k = FutureTask.class;
            stateOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("state"));
            runnerOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("runner"));
            waitersOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("waiters"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
此任務的運行狀態,最初爲NEW。 運行狀態僅在set,setException和cancel方法中轉換爲終止狀態。 在完成期間,狀態可能會採用COMPLETING(正在設置結果時)或INTERRUPTING(僅在中斷正在跑的線程滿足cancel(true)時)的瞬態值。 從這些中間狀態到最終狀態的轉換使用開銷低的有序/惰性寫入,因爲值是唯一的,無法進一步修改。 可能的狀態轉換:
NEW -> COMPLETING -> NORMAL 
NEW -> COMPLETING -> EXCEPTIONAL 
NEW -> CANCELLED 
NEW -> INTERRUPTING -> INTERRUPTED



private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;

/** The underlying callable; nulled out after running */
    private Callable<V> callable;
    /** get()返回的結果對象 or exception to throw from  */
    private Object outcome; // non-volatile, protected by state reads/writes
    /** The thread running the callable; CASed during run() */
    private volatile Thread runner;
    /** Treiber stack of waiting threads */
    private volatile WaitNode waiters;
// 構造函數 ,狀態初始化爲 NEW
public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

// V 是返回的結果類型 。 用一個包裝類實現 Callable
public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

既然實現了 Runnable 肯定有run方法來給線程執行,先看下run方法:

public void run() {
// 就檢查當前狀態是不是New, 如果不是則使用CAS操作將runner屬性設置位當前線程
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            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) 
                    set(result);
            }
        } finally {
// 在finally塊中,我們將runner屬性置爲null,並且檢查有沒有遺漏的中斷,如果發現s >= INTERRUPTING, 說明執行任務的線程有可能被中斷了,因爲s >= INTERRUPTING 只有兩種可能,state狀態爲INTERRUPTING和INTERRUPTED。

有的同學可能就要問了,咱前面已經執行過的set方法或者setException方法不是已經將state狀態設置成NORMAL或者EXCEPTIONAL了嗎?怎麼會出現INTERRUPTING或者INTERRUPTED狀態呢?別忘了,咱們在多線程的環境中,在當前線程執行run方法的同時,有可能其他線程取消了任務的執行,此時其他線程就可能對state狀態進行改寫,這也就是我們在設置終止狀態的時候用putOrderedInt方法,而沒有用CAS操作的原因——我們無法確信在設置state前是處於COMPLETING中間態還是INTERRUPTING中間態。
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
}

//除非已經設置或取消了該Future,否則將此Future的結果設置爲給定值。
成功完成計算後,run方法會在內部調用此方法。  狀態轉換 NEW->COMPLETING->NORMAL 
protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            // 
            finishCompletion();
        }
    }

// 等待棧中移除節點,通知所有等待線程,調用 down(),並把 callable = null
private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) { // 等待棧不爲空
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) { // 將waitersOffset從q賦值爲null
                for (;;) { // 死循環釋放所有等待線程,線程有些線程在 run這個方法,有些可能想取消
                    Thread t = q.thread; 
                    if (t != null) { // 釋放當前執行完的節點
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next; 
                }
                break;
            }
        }

        done(); // FutureTask 是空方法

        callable = null;        // to reduce footprint
    }

 

看下獲取結果的代碼get():

// FutureTask中會涉及到兩類線程,一類是執行任務的線程,它只有一個,FutureTask的run方法就由該線程來執行;一類是獲取任務執行結果的線程,它可以有多個,這些線程可以併發執行,每一個線程都是獨立的,都可以調用get方法來獲取任務的執行結果。如果任務還沒有執行完,則這些線程就需要進入Treiber棧中掛起,直到任務執行結束,或者等待的線程自身被中斷。
public V get() throws InterruptedException, ExecutionException {
       // 最初狀態爲 NEW = 0 COMPLETING = 1
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
// 根據當前state狀態,返回的結果,或拋出異常
        return report(s);
    }

// 等待結果執行完成,或者終止或者中斷或者超時
private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) { // 死循環等待一個結果
            if (Thread.interrupted()) { // 線程中斷則等待池中移除節點,然後拋出異常
                removeWaiter(q);
                throw new InterruptedException();
            }

            int s = state;
            if (s > COMPLETING) { // 線程運行結束
                if (q != null)
                    q.thread = null; // 方便GC
                return s;
            }
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield(); // 線程執行完成,給調度程序的提示是當前線程願意放棄對處理器的當前使用。調度程序可以隨意忽略此提示。
            else if (q == null) // 第一次執行則先創建等待節點,結構是 線程和next節點。 和AQS 隊列類似
                q = new WaitNode();
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q); // CAS操作將新建的q節點添加到waiters鏈表的頭節點之前,其實就是Treiber棧的入棧操作
            else if (timed) { 
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) { // 超時則移出等待隊列,返回結果
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }

任務取消執行:


public boolean cancel(boolean mayInterruptIfRunning) { // 根據狀態轉換,只有state爲NEW 才能取消
    if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    try {    // in case call to interrupt throws exception
        if (mayInterruptIfRunning) {
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt(); // 中斷當前線程
            } finally { // final state
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
        finishCompletion(); // 中斷操作完成後,喚醒所有在棧中等待的線程。
    }
    return true;
}

總結

FutureTask 可以讓主線程去執行其他操作,但是 get 結果是同步等待結果計算完成,想異步獲取執行結果,可以看下博主的 這篇文章 Java 源碼解析 ---- ExecutorCompletionService 進一步瞭解

 

參考資料

JDK 8 源碼

https://segmentfault.com/a/1190000016572591

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