FutureTask類(源碼解讀)

目錄

一.源碼解讀

1.1 FutureTask(Callable callable)

1.2 FutureTask(Runnable runnable, V result)

1.3 FutureTask類的一些常量和屬性

1.3.1 任務當前狀態 state

1.3.2 任務7種狀態屬性:

1.3.3 存放返回結果 outcome:

1.3.4 指向傳入的實現了Callable接口的對象索引 callable

1.3.5 運行Callable的線程 runner

1.3.6 一個帶向鏈表結構的線程鏈表中的一個節點(此部分博主還不太明白)

1.4 6個public修飾的方法

1.4.1 isCancelled()

1.4.2 isDone()

1.4.3 cancel()

1.4.4 get()

1.4.5 get(long timeout, TimeUnit unit)

1.4.6 run()

1.5 5個private修飾的方法

1.5.1 finishCompletion()

1.5.2 awaitDone(boolean timed, long nanos)

1.5.3 removeWaiter(WaitNode node)

1.5.4 report(int s)

1.5.5 handlePossibleCancellationInterrupt(int s)

1.6 4個protected修飾的方法

1.6.1 set()

1.6.2 setException(Throwable t)

1.6.3 done()

1.6.4 runAndReset()

1.7 靜態代碼塊

1.8 靜態內部類


FutureTask類實現了RunnableFuture接口相關功能。RunnableFuture接口源碼解讀

此類的對象可以看成是一個可以對任務進行一些操作的實體,或者封裝。

FutureTask類中:

2個有參構造器。

6個public修飾的方法。

5個private修飾的私有方法。

4個protected修飾的方法

1個靜態內部類。

1個靜態代碼塊。

一.源碼解讀

先來看看此類的兩個有參的構造器

1.1 FutureTask(Callable<V> callable)

    /**
     * Creates a {@code FutureTask} that will, upon running, execute the
     * given {@code Callable}.
     *
     * @param  callable the callable task
     * @throws NullPointerException if the callable is null
     */
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

此構造器作用是傳入一個實現了Callable接口的的任務類,初始化該FutureTask能操作的任務類

 

1.2 FutureTask(Runnable runnable, V result)

    /**
     * Creates a {@code FutureTask} that will, upon running, execute the
     * given {@code Runnable}, and arrange that {@code get} will return the
     * given result on successful completion.
     *
     * @param runnable the runnable task
     * @param result the result to return on successful completion. If
     * you don't need a particular result, consider using
     * constructions of the form:
     * {@code Future<?> f = new FutureTask<Void>(runnable, null)}
     * @throws NullPointerException if the runnable is null
     */
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

如上圖此構造器的作用是傳入一個實現了Runnable接口的任務類,並設置當此任務類運行完成後,任務類需要向調用此類的get方法的對象返回的 結果(result)。

此構造器會調用Executors類的靜態方法 callable(Runnable task, T result) 如下圖

    /**
     * Returns a {@link Callable} object that, when
     * called, runs the given task and returns the given result.  This
     * can be useful when applying methods requiring a
     * {@code Callable} to an otherwise resultless action.
     * @param task the task to run
     * @param result the result to return
     * @param <T> the type of the result
     * @return a callable object
     * @throws NullPointerException if task null
     */
    public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }

而此靜態方法會返回一個實現了Executors中的靜態內部類:RunnableAdapter。如下圖

    /**
     * A callable that runs given task and returns given result
     */
    static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }

如上圖可以看見,此類就是實現了Callable接口,使得實現Runnable的類也能獲取返回值,雖然此返回值是初始化任務時設置的。之前只實現Runnable接口的類,線程執行完這些類後是不會有返回值的。此RunnableAdapter<T>也是對適配器模式的實現.

以上模塊我們能知道,此構造器的作用就是讓只實現了Runnable接口的任務類,也能擁有我們設置的result返回值。類型是個泛型。因此返回的對象很寬泛,並不是說只能返回字符串等。

由於當前FutureTask類實現了RunnableFuture接口,當然需要實現它對應的功能,看完構造器,我們來看看他public修飾的方法。來看看這些方法可以對傳入的(實現了Callable或Runnable的)任務類做出那些操作。

以下對傳入的任務類統稱任務X(不論實現了什麼接口的任務類)

1.3 FutureTask類的一些常量和屬性

    /**
     * The run state of this task, initially NEW.  The run state
     * transitions to a terminal state only in methods set,
     * setException, and cancel.  During completion, state may take on
     * transient values of COMPLETING (while outcome is being set) or
     * INTERRUPTING (only while interrupting the runner to satisfy a
     * cancel(true)). Transitions from these intermediate to final
     * states use cheaper ordered/lazy writes because values are unique
     * and cannot be further modified.
     *
     * Possible state transitions:
     * 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;

在進入第1.4之前我們需要先了解一下此類的一些屬性和字段。

上圖的屬性都被private修飾,意義它們是此類自己使用的屬性,不對外暴露。

1.3.1 任務當前狀態 state

首先第一個state,顧名思義,爲當前任務X的狀態。它被volatile修飾,意義是同時只能被一個線程操作他的狀態。

注意註釋中說明了只有這四種狀態的變化:

  1   * NEW -> COMPLETING -> NORMAL
  2   * NEW -> COMPLETING -> EXCEPTIONAL
  3   * NEW -> CANCELLED
  4   * NEW -> INTERRUPTING -> INTERRUPTED

1.3.2 任務7種狀態屬性:

NEW:代表此任務X正在從調用start()方法到任務正在運行的狀態。

COMPLETING:代表此任務X運行結束,返回結果的時候,此時結果還未裝載到outcome字段中(下面會提到)。完成中!

NORMAL:代表任務X執行成功,outcome字段保存正常返回值。

EXCEPTIONAL:代表任務X執行拋異常,outcome字段保存異常信息。

CANCELLED:任務X從start()方法開始到任務運行完成之前,用戶調用了cancel(false)方法。因爲cancel(false)會允許正在執行任務的線程執行完任務。所以任務最後會被執行完。所以從cancel(false)方法被調用到任務執行完畢。任務狀態都會被置爲CANCELLED。 請參考future接口中cancel的定義:Future接口(源碼解讀)

INTERRUPTING:任務X從start()方法開始到任務運行完成之前,用戶調用了cancel(true)方法。此方法會調用interrupt()方法把中斷標誌置爲true。當線程輪詢到此標誌位爲true時,會做相關操作,比如真正的中斷線程。例如說執行return或其他操作,拋出異常等。有關interrupt()方法可以參考博客:(先做個假鏈接,這裏我要是忘了寫,你們要麼提醒我一下,要麼百度一下)。

而在調用cancel(true)方法到任務被中斷之前,狀態標誌位爲INTERRUPTING

INTERRUPTED:當線程被真正中斷後,任務的狀態標誌爲此標誌。

1.3.3 存放返回結果 outcome

/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes

1.3.4 指向傳入的實現了Callable接口的對象索引 callable

/** The underlying callable; nulled out after running */

private Callable<V> callable;

1.3.5 運行Callable的線程 runner

/** The thread running the callable; CASed during run() */
private volatile Thread runner;

1.3.6 一個帶向鏈表結構的線程鏈表中的一個節點(此部分博主還不太明白)

    /** Treiber stack of waiting threads */
    private volatile WaitNode waiters;

1.4 6個Public修飾的方法

1.4.1 isCancelled()

當任務狀態大於等於CANCELLED時,返回true。

    public boolean isCancelled() {
        return state >= CANCELLED;
    }

1.4.2 isDone()

當任務狀態只要不等於NEW時,返回true。由此可以說明,除了NEW的狀態,都可視爲任務已經運行完成了。至於完成成功與失敗不做考慮。

    public boolean isDone() {
        return state != NEW;
    }

1.4.3 cancel()

實現了future接口的方法,此FutureTask類實現了RunnableFuture接口,而RunnableFuture接口又繼承了future接口。future接口中有cancel方法。

    public boolean cancel(boolean mayInterruptIfRunning) {
        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;
    }

首先我們來看一下這個方法的第一個if判斷:

當任務X當前狀態不爲NEW時,這裏是個&&,因此後面的compareAndSwapInt方法並不會執行。

cancel方法直接返回false。

(1)意味着任務X當前的狀態不爲NEW時,不能使用cancel方法,cancel方法不會執行任何操作並返回false。

當任務X當前的狀態爲NEW時,我們再來看看這個方法。

UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)

這個方法是Unsafe類的(native)本地方法。也是對CAS思想的實現。關於CAS:

    /**
     * Atomically update Java variable to <tt>x</tt> if it is currently
     * holding <tt>expected</tt>.
     * @return <tt>true</tt> if successful
     */
    public final native boolean compareAndSwapInt(Object o, long offset,
                                                  int expected,
                                                  int x);

此方法的意思是,當存放對象o中偏移量爲offset的值與期望值excepted相等時,把對象o中偏移量爲offset的值修改爲x

至此我們返回上面的if判斷:

(2)意味着任務X當前的狀態爲NEW時,且傳入的mayInterruptIfRunning爲false時。任務的狀態被切換爲CANCELLED

且繼續運行try部分代碼,此時mayInterruptIfRunning爲false,所以直接執行finally代碼塊中的finishCompletion()方法:

這裏我們轉到1.5.9節。好了看完finishCompletion()方法我們知道了只要執行cancel方法,那麼waiters鏈表中所有節點的任務都會被取消。

(3)意味着任務X當前的狀態爲NEW時,且傳入的mayInterruptIfRunning爲true時。任務的狀態被切換爲INTERRUPTING並且告知線程需要被中斷。執行了interrupt方法。這裏我們繼續來看看UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);

    /** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)}  */
    public native void    putOrderedInt(Object o, long offset, int x);

這個方法也是一個本地方法,意味着讓虛擬機去設置內存屏障,避免指令重排序。作用是把offset位置的值修改爲x。而且只有當offset位置的值被volatile修飾時,它被更新爲x後才能立即從工作內存刷新到主內存中,這裏大家應該能發現1.3.1中state就是被volatile修飾的。這裏我們需要了解JMM的知識。

當然在這裏就是把狀態更新爲INTERRUPTED

看完上面3點,我們走完了1.3.1中第3種和第4種狀態變化。

1.4.4 get()

    /**
     * @throws CancellationException {@inheritDoc}
     */
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

這方法是實現父接口的父接口future中的get方法。調用此方法的線程會阻塞到得到任務返回的結果。

裏面用了awaitDone方法:請看1.6.1。

此方法意味着當任務未完成時纔會去調用awaitDone的方法,看完1.6.1我們也知道,此方法會在大於COMPLETING時返回任務狀態。其他狀態要麼yield要麼park了。當大於COMPLETING狀態時(可再次查看1.3.1查看狀態變化)不進入awaitDone方法直接返回狀態。因爲只要完成之後,狀態就更新了,可以直接返回狀態。

1.4.5 get(long timeout, TimeUnit unit)


    /**
     * @throws CancellationException {@inheritDoc}
     */
    public V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        if (unit == null)
            throw new NullPointerException();
        int s = state;
        if (s <= COMPLETING &&
            (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
            throw new TimeoutException();
        return report(s);
    }

此方法和get無參方法差不多,只是當任務未結束時,已經到達了等待時間,那麼會拋出超時異常。

1.4.6 run()

    public void run() {
        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 {
            // 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);
        }
    }

實現了接口的方法,此方法纔是核心運行任務的方法,看源碼,它首先判斷任務是否爲NEW,不爲NEW就直接返回了,從此可知,重複運行run方法是沒有用的,同一個任務只運行一次。

如果當前的執行Callable的線程runner爲null,那麼他會讓當前線程去成爲runner。執行Callable。這也是run的意義。當然如果不爲null,已經有runner去執行任務了,那麼也直接run方法也直接return。

try塊中的代碼就是執行Callable的call方法。並獲取結果result。不拋異常就調用set方法,那麼我們來看一下set方法:見1.7.1節

那麼當拋出異常我們需要調用setException()方法:

1.5 5個private修飾的方法

1.5.1 finishCompletion()

此方法是私有的供FutureTask類自己使用,它的作用是釋放waiters單鏈表中所有節點的線程和任務資源。

    /**
     * Removes and signals all waiting threads, invokes done(), and
     * nulls out callable.
     */
    private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                for (;;) {//循環完所有waiters鏈表的節點,讓線程置空,並喚醒它。
                    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();

        callable = null;        // to reduce footprint
    }

在看這個方法的源碼之前我們先看一下靜態代碼塊部分,看完靜態代碼塊部分我們看for循環,這個for循環首先定義一個WaitNode q,讓他指向一個waiters節點,並且不能爲空(這也是循環條件),;後不寫任何自增的代碼。接下來獲取節點中的線程t,如果t不爲空,那麼讓它變爲空,接下來我們看看這個方法:LockSupport.unpark(t);

    /**
     * Makes available the permit for the given thread, if it
     * was not already available.  If the thread was blocked on
     * {@code park} then it will unblock.  Otherwise, its next call
     * to {@code park} is guaranteed not to block. This operation
     * is not guaranteed to have any effect at all if the given
     * thread has not been started.
     *
     * @param thread the thread to unpark, or {@code null}, in which case
     *        this operation has no effect
     */
    public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

這方法就是對判斷thread不爲空才繼續調用UNSAFE的unpark方法,我們再往下跟:

    /**
     * Unblock the given thread blocked on <tt>park</tt>, or, if it is
     * not blocked, cause the subsequent call to <tt>park</tt> not to
     * block.  Note: this operation is "unsafe" solely because the
     * caller must somehow ensure that the thread has not been
     * destroyed. Nothing special is usually required to ensure this
     * when called from Java (in which there will ordinarily be a live
     * reference to the thread) but this is not nearly-automatically
     * so when calling from native code.
     * @param thread the thread to unpark.
     *
     */
    public native void unpark(Object thread);

 看上圖我們看見這又是一個native方法,此方法的作用是給傳入此方法的thread線程一個許可,代表它可以甦醒了。不調用此方法,如果此方法調用了park方法,他將會一直被阻塞到park操作上。

繼續回到上面的finishCompletion()方法。我們看見代碼中q.thread = null; LockSupport.unpark(t);首先把節點的線程置空,再把線程喚醒,相當於是把節點線程的任務結束掉,再喚醒線程,那麼線程就沒有任務在執行了。繼續往下看能知道後面的代碼就是不斷都往後遍歷節點,並釋放資源。把每個節點的next都置空也是爲了JVM在進行可達性分析算法時,能回收掉相關資源。

在這裏我們不要忘了回到上面的cancel方法。我們也許是從那裏跳過來的。

1.5.2 awaitDone(boolean timed, long nanos)

此方法爲一個等待任務執行結束並返回任務狀態的作用。可以設置最大等待時間,超過等待時間,直接返回任務狀態並結束任務。

    /**
     * Awaits completion or aborts on interrupt or timeout.
     *
     * @param timed true if use timed waits
     * @param nanos time to wait, if timed
     * @return state upon completion
     */
    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;
                return s;
            }
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            else if (q == null)
                q = new WaitNode();
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            else if (timed) {
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }

看以上源碼能得知此方法通過傳入的布爾變量timed判斷是否要設置等待超時參數。timed爲true時,通過當前系統時間加上傳入的nano等待時長,得到死亡線時間,for循環中,先判斷當前線程是否被中斷,中斷則移除任務節點,並報錯。這裏有removeWaiter()方法:請見1.5.3

如果未被中斷,那麼判斷當前狀態是否大於COMPLETING,大於時,返回任務狀態,並釋放線程。意味着當線程執行完成,如下圖,awaitDone方法會返回正常或異常的狀態。

代碼繼續往下看,當前狀態是COMPLETING時,那麼執行Thread.yield();讓線程進入就緒狀態與其他線程再次競爭運行機會。下面判斷queue的狀態,意味for死循環中只會執行一次 queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);這段代碼。循環中沒有讓queue再變爲false的代碼。除非CAS失敗,意思就是線程不斷輪訓查看waiters節點的狀態,當任務完成後,就把waiters節點置空,並返回狀態。如果設置了timed狀態爲true,那麼當死亡線時間到達,會直接移除節點任務,並返回任務狀態。最後,當以上所有狀態都不滿足時,此線程會被park方法阻塞。

這裏記得返回get方法。此方法相當於就是對get方法的實現。

1.5.3 removeWaiter(WaitNode node)

這個方法的註釋是:

讓超時的或中斷的節點unlink掉也就是從這個鏈表中取出。來避免累計垃圾。

當不使用CAS時,這些節點時不能分割的,所以當釋放器去遍歷時,是不會對鏈表有影響。

爲避免已刪除的節點分割對未分割節點的影響,鏈表會重新整理。整理的過程是緩慢的,所以鏈表不能過長以致超過整理所計劃消耗的資源。

    /**
     * Tries to unlink a timed-out or interrupted wait node to avoid
     * accumulating garbage.  Internal nodes are simply unspliced
     * without CAS since it is harmless if they are traversed anyway
     * by releasers.  To avoid effects of unsplicing from already
     * removed nodes, the list is retraversed in case of an apparent
     * race.  This is slow when there are a lot of nodes, but we don't
     * expect lists to be long enough to outweigh higher-overhead
     * schemes.
     */
    private void removeWaiter(WaitNode node) {
        if (node != null) {
            node.thread = null;
            retry:
            for (;;) {          // restart on removeWaiter race
                for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
                    s = q.next;
                    if (q.thread != null)
                        pred = q;
                    else if (pred != null) {
                        pred.next = s;
                        if (pred.thread == null) // check for race
                            continue retry;
                    }
                    else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                          q, s))
                        continue retry;
                }
                break;
            }
        }
    }

1.5.4 report(int s)

    /**
     * Returns result or throws exception for completed task.
     *
     * @param s completed state value
     */
    @SuppressWarnings("unchecked")
    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);
    }

此方法就是獲取任務狀態,並判斷,當任務爲正常完成NORMAL時返回狀態,當任務s大於等於CANCELLED時,那麼也許是4,5,6。當小於CANCELLED時也許是3,爲什麼沒有0和1呢因爲private修飾的report方法在每次調用的時候都已經判斷大於COMPLETING  了 所以。這個report確實是對的,當狀態爲EXCEPTIONAL  就報執行異常ExecutionException((Throwable)x)

並拋出x異常信息,因爲之前我們提到當狀態爲EXCEPTIONAL  時,會把異常信息裝到任務狀態中。可見setException方法。

    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;

1.5.5 handlePossibleCancellationInterrupt(int s)

    /**
     * Ensures that any interrupt from a possible cancel(true) is only
     * delivered to a task while in run or runAndReset.
     */
    private void handlePossibleCancellationInterrupt(int s) {
        // It is possible for our interrupter to stall before getting a
        // chance to interrupt us.  Let's spin-wait patiently.
        if (s == INTERRUPTING)
            while (state == INTERRUPTING)
                Thread.yield(); // wait out pending interrupt

        // assert state == INTERRUPTED;

        // We want to clear any interrupt we may have received from
        // cancel(true).  However, it is permissible to use interrupts
        // as an independent mechanism for a task to communicate with
        // its caller, and there is no way to clear only the
        // cancellation interrupt.
        //
        // Thread.interrupted();
    }

1.6 4個protected修飾的方法

1.6.1 set()

    /**
     * Sets the result of this future to the given value unless
     * this future has already been set or has been cancelled.
     *
     * <p>This method is invoked internally by the {@link #run} method
     * upon successful completion of the computation.
     *
     * @param v the value
     */
    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }

看完上面的內容,相信大家對這個UNSAFE裏的方法很熟悉了。那麼set方法就是把NEW狀態更新爲COMPLETING,更新成功就把v結果值賦值給outcome,賦值完成後,再把狀態更新爲NORMAL。最後釋放各種資源調用finishCompletion方法。見1.5.9節。

看完set方法,我們走完了1.3.1中第1種狀態變化。

1.6.2 setException(Throwable t)

    /**
     * Causes this future to report an {@link ExecutionException}
     * with the given throwable as its cause, unless this future has
     * already been set or has been cancelled.
     *
     * <p>This method is invoked internally by the {@link #run} method
     * upon failure of the computation.
     *
     * @param t the cause of failure
     */
    protected void setException(Throwable t) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }

如果我們看完了set方法再來看此方法,那麼能知道它就是走的1.3.1節中的第二種狀態。

相信看完這裏我們已經走完了1.3.1節任務狀態中的第4種狀態。

1.6.3 done()

此方法是一個回調函數,雖然在本類中此方法沒有任何實現,但是我們仔細觀察會發現,此done方法在本類中被finishCompletion()方法釋放所有資源後最後調用了(請見1.5.1)。然而finishCompletion()又被set方法(請見1.6.1)和setException(Throwable t)和cancel方法(請見1.4.3)調用。這3個方法我們看完就能知道他設置改變了1.3.1中說明會發生的4種狀態變化。從而知道done方法會在每個流程走完的時候被調用,每種流程最後都調用了finishCompletion(),而finishCompletion()最後又會調用done。所以當子類實現此方法後,可以在這方法中寫一些任務執行完需要執行的代碼。finishCompletion()會去回調它。

    /**
     * Protected method invoked when this task transitions to state
     * {@code isDone} (whether normally or via cancellation). The
     * default implementation does nothing.  Subclasses may override
     * this method to invoke completion callbacks or perform
     * bookkeeping. Note that you can query status inside the
     * implementation of this method to determine whether this task
     * has been cancelled.
     */
    protected void done() { }

栗子:task是傳入實現了Runnable或Callable的任務(類)。

        FutureTask<Integer> future = new FutureTask<Integer>(task) {
            @Override
            protected void done() {
                try {
                    System.out.println("任務執行完畢,執行結果爲:" + get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        };

1.6.4 runAndReset()

此方法從名字上很好懂,運行並重置。此方法在本類中沒有被任何其他方法調用

先來看看它的註釋:

在不設置結果的情況下執行計算,然後將此任務重置爲初始狀態,如果計算遇到異常或被取消,則無法執行此操作。這是爲原本會執行多次的任務的需求而設計的。

意思就是當一個任務執行之後會改變一些運行前的環境,而不能重複運行任務,而這個方法就是保證每次run正常執行後,都能重置並再次運行。

    /**
     * Executes the computation without setting its result, and then
     * resets this future to initial state, failing to do so if the
     * computation encounters an exception or is cancelled.  This is
     * designed for use with tasks that intrinsically execute more
     * than once.
     *
     * @return {@code true} if successfully run and reset
     */
    protected boolean runAndReset() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return false;
        boolean ran = false;
        int s = state;
        try {
            Callable<V> c = callable;
            if (c != null && s == NEW) {
                try {
                    c.call(); // don't set result
                    ran = true;
                } catch (Throwable ex) {
                    setException(ex);
                }
            }
        } finally {
            // 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
            s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
        return ran && s == NEW;
    }

 一下代碼我們在run方法中解釋過了,是保證當前只有一個線程在運行任務,不論換哪一個線程來執行任務,任務都只有一個任務。如果當前沒有線程執行本任務,那麼把任務類中記錄的運行本任務線程的變量置爲本線程(此變量被存放在本類偏移量爲runnerOffset的位置)。就是說沒有線程運行本任務那麼就由我來。

        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return false;

此方法對比run方法,他關鍵的地方就是沒有調用set方法。而set方法做了啥呢(參見1.6.1節)?他相當於讓任務狀態進行了 4種狀態的第一種:

NEW -> COMPLETING -> NORMAL

如果不調用set方法的話,任務狀態始終是NEW。所以我們能知道。runAndReset()的核心功能是,在運行完成後並不改變此任務的狀態。以此達到多次運行的效果,比如當前有個需求是運行此任務4次算完成任務。那麼前3次我可以調用runAndReset(),最後以此調用run(),從run方法或runAndReset中我們是能知道只有當狀態爲NEW時,任務纔會運行的。其他狀態時都會直接return了。

補充一下,爲什麼其他狀態我們不考慮呢?因爲其他狀態要麼是中斷要麼就是取消了,要麼是異常了。只有正常運行完成我們纔會繼續任務對吧。因此纔會在返回時有判斷 return ran && s == NEW;代表ran要是true運行成功,並且沒有被取消和中斷。s需要判斷爲NEW纔算reset。

1.7 靜態代碼塊

    // 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"));//獲取當前k類中任務的狀態屬性偏移量
            runnerOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("runner"));//獲取當前執行Callable方法的線程的偏移量
            waitersOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("waiters"));//獲取k類的waiters等待線程節點偏移量
        } catch (Exception e) {
            throw new Error(e);
        }
    }

上面代碼中可以看出,先獲取到了FutureTask類的Class對象k。然後獲取它的state屬性,runner屬性,waiters屬性,的偏移量。這三個屬性都能在類中源碼看見。

1.8 靜態內部類

本類中執行任務的線程等待鏈表的一個節點的構造類。

    /**
     * Simple linked list nodes to record waiting threads in a Treiber
     * stack.  See other classes such as Phaser and SynchronousQueue
     * for more detailed explanation.
     */
    static final class WaitNode {
        volatile Thread thread;
        volatile WaitNode next;
        WaitNode() { thread = Thread.currentThread(); }
    }

 

二.類的使用實踐

待完善...

 

 

 

 

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