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

一、理解

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

二、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保存異步計(jì)算的結(jié)果,該接口提供以下的方法

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

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

三、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實(shí)現(xiàn)了RunnableFuture接口,即FutureTask對象既是一個Future接口的實(shí)例又是一個Runnable接口的實(shí)例。

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

狀態(tài)變化:

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

構(gòu)造器:

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

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

主要源代碼說明:

// 用來保存計(jì)算任務(wù)的返回結(jié)果,或者執(zhí)行過程中拋出的異常。
/** The result to return or exception to throw from get() */
private Object outcome;
//運(yùn)行傳入的callable對象的線程
private volatile Thread runner;
//WaitNode是FutureTask的內(nèi)部類,表示一個阻塞隊(duì)列,如果任務(wù)還沒有執(zhí)行結(jié)束,那么調(diào)用get()獲取結(jié)果的線程會阻塞,在這個阻塞隊(duì)列中排隊(duì)等待。
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狀態(tài)值是否是NEW,不是NEW,說明任務(wù)已經(jīng)被其他線程執(zhí)行,甚至執(zhí)行結(jié)束,或者被取消了,直接返回
        //調(diào)用CAS方法,判斷runnerOffset為null的話,就將當(dāng)前線程保存到runnerOffset中(即把當(dāng)前的線程賦值給runner),
        //如果設(shè)置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 {
                    // 執(zhí)行Callable任務(wù),結(jié)果保存到result中
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    //如果執(zhí)行任務(wù)過程中發(fā)生異常,將調(diào)用setException()設(shè)置異常
                    setException(ex);
                }
                //任務(wù)正常執(zhí)行結(jié)束調(diào)用set(result)保存結(jié)果
                if (ran)
                    set(result);
            }
        } finally {
            //任務(wù)執(zhí)行結(jié)束,runner設(shè)置為null,表示當(dāng)前沒有線程在執(zhí)行這個任務(wù)了
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            //讀取狀態(tài),判斷是否在執(zhí)行的過程中,被中斷了,如果被中斷,處理中斷
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

設(shè)置處理異常:

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

設(shè)置處理結(jié)果:

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

get方法:

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

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

/**
     * 等待任務(wù)完成或者中止或者超時
     *
     * @param timed 是否啟用等待時長
     * @param nanos 等待時間
     * @return 任務(wù)完成后的狀態(tài)
     */
    private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        //自驅(qū)
        for (;;) {
            //如果調(diào)用線程被阻斷了就從等待的線程棧中移除這個等待節(jié)點(diǎn),然后拋出中斷異常
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }

            //獲取當(dāng)前任務(wù)的狀態(tài)
            int s = state;
            //如果當(dāng)前任務(wù)已經(jīng)執(zhí)行完成(正常或異常結(jié)束),不在阻塞,直接返回任務(wù)狀態(tài)
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            //如果任務(wù)結(jié)束,但是最終結(jié)果還沒保存下來,可以暫時讓出CPU
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            //如果等待節(jié)點(diǎn)q=null,就創(chuàng)建一個等待節(jié)點(diǎn)
            else if (q == null)
                q = new WaitNode();
            //如果這個等待節(jié)點(diǎn)還沒有加入等待隊(duì)列,就加入隊(duì)列頭
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            //如果設(shè)置了超時等待時間
            else if (timed) {
                nanos = deadline - System.nanoTime();
                //如果超出了等待時間,停止阻塞,返回當(dāng)前任務(wù)的狀態(tài)
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                //阻塞特定時間
                LockSupport.parkNanos(this, nanos);
            }
            else
                //阻塞
                LockSupport.park(this);
        }
    }
private V report(int s) throws ExecutionException {
        //獲取當(dāng)前任務(wù)的結(jié)果
        Object x = outcome;
        //如果任務(wù)的狀態(tài)是正常完成,則返回結(jié)果
        if (s == NORMAL)
            return (V)x;
        //如果任務(wù)被取消了,則拋出取消異常
        if (s >= CANCELLED)
            throw new CancellationException();
         //否則異常結(jié)束,outcome里面保存的是異常結(jié)果,將異常拋出
        throw new ExecutionException((Throwable)x);
    }

狀態(tài)值不為初始狀態(tài),表示完成

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

狀態(tài)值為CANCELLED、INTERRUPTING、INTERRUPTED表示已經(jīng)取消,反之為未取消

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

cancel方法:

public boolean cancel(boolean mayInterruptIfRunning) {
         //判斷當(dāng)前任務(wù)的狀態(tài)是否為新建狀態(tài),如果不是說明任務(wù)已經(jīng)正常或異常結(jié)束了,直接返回取消失敗
        //如果任務(wù)是NEW狀態(tài),根據(jù)mayInterruptIfRunning嘗試將任務(wù)狀態(tài)改成CANCELLED或者INTERRUPTING,更改失敗返回false
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    // in case call to interrupt throws exception
            //如果取消的時候嘗試打斷任務(wù)的執(zhí)行
            if (mayInterruptIfRunning) {
                try {
                    //獲取執(zhí)行當(dāng)前任務(wù)的線程
                    Thread t = runner;
                    if (t != null)
                        //調(diào)用interrupt方法打斷線程
                        t.interrupt();
                } finally { // final state
                    //將任務(wù)的狀態(tài)改為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方法中都有調(diào)用,分析代碼可以知道這個方法是在任務(wù)的狀態(tài)變成終態(tài)的時候會被調(diào)用。該方法主要做了三件事:
1.遍歷waiters等待隊(duì)列,調(diào)用LockSupport.unpark(t)喚醒等待返回結(jié)果的線程,釋放資源。
2.調(diào)用done(),這個方法什么都沒有做,不過子類可以實(shí)現(xiàn)這個方法,做一些額外的操作。
3.設(shè)置callable為null,callable是FutureTask封裝的任務(wù),任務(wù)執(zhí)行完,釋放資源。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容