Java多線程詳解(七)--Future

一、理解

future模式是多線程開發中非常常見的一種設計模式,它的核心思想是異步調用。當我們需要調用一個函數方法時,如果這個函數執行很慢,那么我們就要進行等待。但有時候我們可能并不著急著要結果。因此,我們可以讓被調用者立即返回,讓他在后臺慢慢處理這個請求。對于調用者來說,則可以先處理一些其他任務,在真正需要數據的場合再去嘗試獲得需要的數據。
對于future模式來說,雖然它無法立即給你需要的數據。但是,它會返回給你一個契約,將來,你可以憑借這個契約去重新獲取你需要的信息。

二、Future源代碼

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Future保存異步計算的結果,該接口提供以下的方法

1.cancel(boolean mayInterruptIfRunning): 試圖取消執行的任務,參數為true時直接中斷正在執行的任務,否則直到當前任務執行完成,成功取消后返回true,否則返回false
2.isCancelled:判斷任務是否在正常執行完前被取消的,如果是則返回true
3.isDone: 判斷任務是否已完成

  1. get(): 等待計算結果的返回(阻塞方法),如果計算被取消了則拋出異常
  2. get(long timeout, TimeUnit unit):設定計算結果的返回時間,如果在規定時間內沒有返回計算結果則拋出異常

三、RunnableFuture

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

RunnableFuture繼承Runnable、Future兩個接口,Runnable的方法run()為抽象方法。

四、FutureTask

FutureTask實現了RunnableFuture接口,即FutureTask對象既是一個Future接口的實例又是一個Runnable接口的實例。

private volatile int state;
    //表示這是一個新的任務,或者還沒有執行完的任務,是初始狀態。
    private static final int NEW          = 0;
    //表示任務執行結束(正常執行結束,或者發生異常結束),但是還沒有將結果保存到outcome中。是一個中間狀態。
    private static final int COMPLETING   = 1;
    //示任務正常執行結束,并且已經把執行結果保存到outcome字段中。是一個最終狀態。
    private static final int NORMAL       = 2;
    //表示任務發生異常結束,異常信息已經保存到outcome中,這是一個最終狀態。
    private static final int EXCEPTIONAL  = 3;
    //任務在新建之后,執行結束之前被取消了,但是不要求中斷正在執行的線程,也就是調用了cancel(false),任務就是CANCELLED狀態
    private static final int CANCELLED    = 4;
    //任務在新建之后,執行結束之前被取消了,并要求中斷線程的執行,也就是調用了cancel(true),這時任務狀態就是INTERRUPTING
    private static final int INTERRUPTING = 5;
    //調用cancel(true)取消異步任務,會調用interrupt()中斷線程的執行,然后狀態會從INTERRUPTING變到INTERRUPTED。
    private static final int INTERRUPTED  = 6;

狀態變化:

     * Possible state transitions:
     * NEW -> COMPLETING -> NORMAL
     * NEW -> COMPLETING -> EXCEPTIONAL
     * NEW -> CANCELLED
     * NEW -> INTERRUPTING -> INTERRUPTED

構造器:

//傳入一個Callable對象,可以通過get方法獲取到結果
public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

//傳入一個Runnable對象和一個返回的結果,該Runnable對象會通過Executors.callable(runnable, result)方法封裝成Callable對象;result作為get方法返回的結果
public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

主要源代碼說明:

// 用來保存計算任務的返回結果,或者執行過程中拋出的異常。
/** The result to return or exception to throw from get() */
private Object outcome;
//運行傳入的callable對象的線程
private volatile Thread runner;
//WaitNode是FutureTask的內部類,表示一個阻塞隊列,如果任務還沒有執行結束,那么調用get()獲取結果的線程會阻塞,在這個阻塞隊列中排隊等待。
private volatile WaitNode waiters;
/**
     * 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(); }
    }

run()方法:

public void run() {
         //state != NEW:state狀態值是否是NEW,不是NEW,說明任務已經被其他線程執行,甚至執行結束,或者被取消了,直接返回
        //調用CAS方法,判斷runnerOffset為null的話,就將當前線程保存到runnerOffset中(即把當前的線程賦值給runner),
        //如果設置runnerOffset失敗,就直接返回
        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 {
                    // 執行Callable任務,結果保存到result中
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    //如果執行任務過程中發生異常,將調用setException()設置異常
                    setException(ex);
                }
                //任務正常執行結束調用set(result)保存結果
                if (ran)
                    set(result);
            }
        } finally {
            //任務執行結束,runner設置為null,表示當前沒有線程在執行這個任務了
            // 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);
        }
    }

設置處理異常:

protected void setException(Throwable t) {
        //通過CAS操作將當前線程的stateOffset(即state)從NEW置為COMPLETING
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            //將異常信息保存到outcome
            outcome = t;
            //設置狀態為EXCEPTIONAL
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }

設置處理結果:

protected void set(V v) {
        //把state狀態從NEW設置為COMPLETING(0->1)
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            //把處理結果存放在outcome里
            outcome = v;
            //將當前任務的狀態改成NORMAL-2
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }

get方法:

public V get() throws InterruptedException, ExecutionException {
        int s = state;
        //狀態值如果小于COMPLETING,表示未執行完成,阻塞線程進行等待
        //如果任務還未執行完成(任務狀態為NEW,從run方法中可以看到只有任務執行結束,或者發生異常的時候,state才會被設置成COMPLETING)
        if (s <= COMPLETING)
            //調用awaitDone(false, 0L),進入阻塞狀態
            s = awaitDone(false, 0L);
        //返回結果
        return report(s);
    }

一般情況下,執行任務的線程和獲取結果的線程不會是同一個,當我們在主線程或者其他線程中,獲取計算任務的結果時,就會調用get方法,如果這時計算任務還沒有執行完成,調用get()的線程就會阻塞等待。

/**
     * 等待任務完成或者中止或者超時
     *
     * @param timed 是否啟用等待時長
     * @param nanos 等待時間
     * @return 任務完成后的狀態
     */
    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;
            }
            //如果任務結束,但是最終結果還沒保存下來,可以暫時讓出CPU
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            //如果等待節點q=null,就創建一個等待節點
            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);
        }
    }
private V report(int s) throws ExecutionException {
        //獲取當前任務的結果
        Object x = outcome;
        //如果任務的狀態是正常完成,則返回結果
        if (s == NORMAL)
            return (V)x;
        //如果任務被取消了,則拋出取消異常
        if (s >= CANCELLED)
            throw new CancellationException();
         //否則異常結束,outcome里面保存的是異常結果,將異常拋出
        throw new ExecutionException((Throwable)x);
    }

狀態值不為初始狀態,表示完成

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

狀態值為CANCELLED、INTERRUPTING、INTERRUPTED表示已經取消,反之為未取消

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

cancel方法:

public boolean cancel(boolean mayInterruptIfRunning) {
         //判斷當前任務的狀態是否為新建狀態,如果不是說明任務已經正常或異常結束了,直接返回取消失敗
        //如果任務是NEW狀態,根據mayInterruptIfRunning嘗試將任務狀態改成CANCELLED或者INTERRUPTING,更改失敗返回false
        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)
                        //調用interrupt方法打斷線程
                        t.interrupt();
                } finally { // final state
                    //將任務的狀態改為INTERRUPTED
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }

finishCompletion方法:

private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                for (;;) {
                    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
    }

這個方法在run方法和cancel方法中都有調用,分析代碼可以知道這個方法是在任務的狀態變成終態的時候會被調用。該方法主要做了三件事:
1.遍歷waiters等待隊列,調用LockSupport.unpark(t)喚醒等待返回結果的線程,釋放資源。
2.調用done(),這個方法什么都沒有做,不過子類可以實現這個方法,做一些額外的操作。
3.設置callable為null,callable是FutureTask封裝的任務,任務執行完,釋放資源。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。