Java 多線程 --- FutureTask 源碼分析__Java

來源:互聯網
上載者:User

在多線程編程中,建立線程可以直接繼承Thread,也可以實現Runnable介面。但是這2種方式都有一個缺陷就是:在執行完任務之後無法擷取執行結果。
如果需要擷取執行結果,就必須通過共用變數或者使用線程通訊的方式來達到效果,這樣使用起來就比較麻煩。

而自從Java 1.5開始,就提供了Callable和Future,FutureTask,通過它們可以在任務執行完畢之後得到任務執行結果,今天我們就來看看FutureTask 是如何?的。 FutureTask (jdk 1.8)

從名字上我們可以知道,FutureTask 是一個可執行檔task,同時也擁有Future 的特性,可以擷取任務執行的結果,首先我們先看看如何使用FutureTask,這裡會簡單的涉及線程池的概念。 FutureTask 使用

public class FutureTaskDemo {    // 非同步任務    static class Task implements Callable<Integer> {        @Override        public Integer call() throws Exception {            System.out.println(Thread.currentThread().getName());            return 100;        }    }    public static void main(String[] args) {        Task task = new Task();        //使用FutureTask        FutureTask<Integer> futureTask = new FutureTask<Integer>(task);        // 建立線程池        ExecutorService executor = Executors.newCachedThreadPool();        //非同步執行任務        executor.execute(futureTask);        //使用Future        Future<Integer> future = executor.submit(task);        try {            // 阻塞,等待非同步任務執行完畢,擷取非同步任務的傳回值            System.out.println("FutureTask result:" + futureTask.get());            System.out.println("Future result:" + future.get());        } catch (Exception e) {            e.printStackTrace();        }    }}

在上面我們分別使用了FutureTask 和Future來擷取傳回值,表面看似乎是不一樣,實際上本質都是一樣,這裡通過submit 提交的Callable 任務,返回的Future 其實是FutureTask,這個可以在 AbstractExecutorService源碼中找到。

public <T> Future<T> submit(Callable<T> task) {    if (task == null) throw new NullPointerException();    // 封裝任務    RunnableFuture<T> ftask = newTaskFor(task);    // 執行任務    execute(ftask);    return ftask;}protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {    return new FutureTask<T>(runnable, value);}

這個是線程池中的細節,這裡就展開了,回到我們的FutureTask 上面來。 繼承體系


通過繼承關係我們知道 FutureTask 實際上就是Runnable和Future的合體。 資料結構

/** * 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; // 中斷完成/** The underlying callable; nulled out after running *///Callable 任務private Callable<V> callable;/** The result to return or exception to throw from get() */// 任務執行結果private Object outcome; /** The thread running the callable; CASed during run() */// 執行線程private volatile Thread runner;/** Treiber stack of waiting threads */// 調用get() 阻塞等待的線程節點private volatile WaitNode waiters;

主要包含了任務執行狀態,Callable 任務以及任務執行結果,文檔注釋中對任務狀態進行了很詳細的描述。 FutureTask 重要方法

1、get

public V get() throws InterruptedException, ExecutionException

擷取計算的結果, 若計算沒完成, 進行阻塞等待, 直到 計算結束或線程中斷
2、帶逾時功能的get

V get (long timeout, TimeUnit unit) throwsInterruptedException, ExecutionException, TimeoutException;

擷取計算的結果, 若計算沒完成, 進行逾時等待, 如果發生逾時則拋出TimeoutException 異常。
3、isDone

boolean isDone();

返回計算是否完成 , 若任務完成則返回true。

4、awaitDone

int awaitDone(boolean timed, long nanos) throws InterruptedException

逾時等待任務完成, 傳回值是 future 的state的狀態。
5、取消任務 cancel

public boolean cancel(boolean mayInterruptIfRunning)

當任務處於未啟動狀態時,該方法將導致此任務永遠不會被執行;當任務處於已經啟動狀態時,cancle(true)將以中斷執行此任務線程的方式來嘗試停止任務,cancle(false)將不會對正在執行此任務的線程產生影響(讓正在執行的任務運行完成);當任務處於以完成狀態時,該方法將返回false.

6、任務執行完成回調

protected void done()

當任務執行完成後將會調用該方法,在FutureTask 中是空實現,如果有需要可以重寫該方法。 構造方法

1、指定Callable任務

public FutureTask(Callable<V> callable) {    if (callable == null)        throw new NullPointerException();    this.callable = callable;    this.state = NEW;       //初始化狀態}

2、指定Runnable 任務及返回結果

/** * 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)} */public FutureTask(Runnable runnable, V result) {    this.callable = Executors.callable(runnable, result);    this.state = NEW;       // ensure visibility of callable}
run 方法

既然FutureTask 實現了Runnable 介面,那麼就需要實現其run 方法。

public void run() {    // 判斷 state 是否是new, 防止任務重複執行    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 {                // 調用call方法,返回結果result                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);    }}

整個run 方法不複雜,內部調用callable的call 方法,如果任務執行成功,那麼設定運行結果 設定任務運行結果

protected void set(V v) {    // 更新任務狀態    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {        //設定任務結果        outcome = v;        //更新任務狀態        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state        //收尾工作        finishCompletion();    }}
回調及喚醒阻塞線程

當任務還在執行時,調用get 方法會進行阻塞等待,FutureTask 內部維護了一個線程等待的鏈表,這個我們在分析get 方法的時候再來重點分析。

private void finishCompletion() {    // assert state > COMPLETING;    //FutureTask 內部維護了一個線程阻塞等待鏈表(調用get 方法阻塞等待結果)    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    done();    callable = null;        // to reduce footprint}
get 方法
public V get() throws InterruptedException, ExecutionException {    int s = state;    //檢查任務狀態,如果任務還沒有執行完成,則進行等待    if (s <= COMPLETING)        s = awaitDone(false, 0L);    // 返回結果        return report(s);}

get() 方法中涉及到 awaitDone 方法, 將awaitDone的運行結果賦值給state, 最後report方法根據state值進行返回相應的值, 而awaitDone是整個 FutureTask 啟動並執行核心

/** * 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;        }        //任務執行完成,但是部分參數還未設定完畢(參考run 方法)        else if (s == COMPLETING) // cannot time out yet            Thread.yield(); //讓出cpu,等待        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);    }}

waitDone就是將當前線程加入等待隊列(WaitNode有當前Thread的Thread變數),然後用LockSupport將自己阻塞,等待逾時或者被解除阻塞後,返回狀態(可能任務執行完成,也可能沒有執行完成),如果發生了中斷,則會拋出中斷異常。 cancel 取消任務

public boolean cancel(boolean mayInterruptIfRunning) {    //new 狀態包含了任務還沒執行和任務正在執行(正在執行:call方法正在運行)    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;}

如果state不是new 那麼就退出方法,這時的任務任務是已經完成了 或是被取消了 或是被中斷了
如果state 是new 就設定state 為正在中斷 或是取消狀態 (new 狀態包含了任務正在執行的狀態),如果mayInterruptIfRunning 為true 表示允許中斷正在啟動並執行任務,則進行中斷(中斷,並不能即時取消任務,只是設定中斷標誌位,目標線程需要檢測該標誌位才知道是否發生了中斷)。 report 返回結果

根據任務的狀態,返回不同的結果

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);}
總結

FutureTask 內部擁有一個Callable 任務引用,實際的任務是Callable 對象,FutureTask 定義了多種狀態,通過對狀態的設定和判斷可以知道任務的執行情況。
FutureTask 內部維護了一個等待隊列(鏈表),當調用FutureTask 的get 方法時,如果需要進行阻塞等待,則會把請求線程加入到等待隊列中,當任務執行完成(正常或者異常),則會喚醒等待隊列中的線程,這樣等待線程就可以擷取任務執行結果了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.