Java多執行緒之FutureTask

玩毛線的包子發表於2018-10-13

一:FutureTask是什麼?

建立執行緒的方式,一般是通過繼承Thread或者實現Runnable介面實現,線上程執行結束後如果想獲取返回結果,可以使用Handler等實現執行緒間通訊。而FutureTask可以在執行結束之後直接返回結果。

二:FutureTask結構

FutureTask UML

三:示例

    FutureTask<Integer> futureTask = new FutureTask<Integer>(new         Callable<Integer>()     {
            @Override
            public Integer call() throws Exception {
                // 執行緒開始
                int count = 0;
                for (int i = 0; i < 100; i++) {
                    count += i;
                }
                Thread.sleep(3000);
                // 執行緒結束,返回結果
                return count;
            }
        });
        // 新建執行緒,執行futureTask
        new Thread(futureTask).start();

        try {
            // 獲取返回結果,如果futureTask沒有執行結束,則會堵塞當前執行緒等待
            int result = futureTask.get();
            Log.i("testLog", "The result is " + result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
複製程式碼

原始碼分析

FutureTask.java

    // 狀態
    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;
    // 被執行物件,並可以獲取返回值
    private Callable<V> callable;
    // 執行結果,也可以是異常資訊
    private Object outcome; // non-volatile, protected by state reads/writes
    // 當前執行的執行緒,用於保證run只執行一次
    private volatile Thread runner;
    // 任務未執行完成時,等待佇列
    private volatile WaitNode waiters;
    
    // 建構函式
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
    
    // 建構函式,傳入的Runnable和返回結果
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
    // 是否取消
    public boolean isCancelled() {...}
    // 是否被執行
    public boolean isDone() {...}
    // 取消任務
    public boolean cancel(boolean mayInterruptIfRunning) {...}
    // 獲取結果
    public V get() {...}
    // 獲取結果,如果在給定時間內沒有執行完成,丟擲超時異常
    public V get(long timeout, TimeUnit unit) {...}
    // 執行
    public void run() {...}
複製程式碼

run()方法

    public void run() {
        // 1. 狀態如果不是NEW,說明任務或者已經執行過,或者已經被取消,直接返回
        // 2. 狀態如果是NEW,則嘗試把當前執行執行緒儲存在runner欄位中
        // 如果賦值失敗則直接返回
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    // 執行Callable的call方法,並獲取返回結果
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    // 異常,將異常賦值給返回值
                    setException(ex);
                }
                if (ran)
                    // 正常執行,將結果賦值給返回值
                    set(result);
            }
        } finally {
            runner = null;
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
複製程式碼

get()方法

    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            // 未執行完成,則等待
            s = awaitDone(false, 0L);
        return report(s);
    }
    
    private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        long startTime = 0L;    // Special value 0L means not yet parked
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
            int s = state;
            // 已完成,異常或者被取消。
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            // 已完成,讓出執行緒優先權
            else if (s == COMPLETING)
                Thread.yield();
            // 執行緒被打斷,移除等待佇列,並丟擲異常
            else if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }
            // 如果等待佇列為空,則建立一個
            else if (q == null) {
                if (timed && nanos <= 0L)
                    return s;
                q = new WaitNode();
            }
            // 如果還沒有入佇列,則把當前節點加入waiters首節點並替換原來waiters
            else if (!queued)
                queued = U.compareAndSwapObject(this, WAITERS,
                                                q.next = waiters, q);
            // 需要等待,則計算等待時間
            else if (timed) {
                final long parkNanos;
                if (startTime == 0L) { // first time
                    startTime = System.nanoTime();
                    if (startTime == 0L)
                        startTime = 1L;
                    parkNanos = nanos;
                } else {
                    long elapsed = System.nanoTime() - startTime;
                    if (elapsed >= nanos) {
                        removeWaiter(q);
                        return state;
                    }
                    parkNanos = nanos - elapsed;
                }
                if (state < COMPLETING)
                    LockSupport.parkNanos(this, parkNanos);
            }
            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();
        throw new ExecutionException((Throwable)x);
    }
複製程式碼

任務取消

    public boolean cancel(boolean mayInterruptIfRunning) {
        if (!(state == NEW &&
              U.compareAndSwapInt(this, STATE, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    
            // 打斷執行緒 
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        // interrupt()方法只是設定中斷標誌位
                        //如果被中斷的執行緒處於sleep()、wait()或者join()邏輯中則會丟擲InterruptedException異常。
                        t.interrupt();
                } finally { // final state
                    U.putOrderedInt(this, STATE, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }
    
    // 喚醒所有等待執行緒,移除所有等待佇列
    private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (U.compareAndSwapObject(this, WAITERS, 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
    }

複製程式碼

五:小結

  1. FutureTask是可以被執行緒執行並獲取返回執行結果的。
  2. 其他執行緒在獲取FutureTask的執行結果時,如果FutureTask未執行結束,則會被堵塞等待的。
  3. FutureTask呼叫cancel(true)並不一定能停止當前的執行緒。

參考:

深入學習FutureTask

Java併發學習(四)-sun.misc.Unsafe

UML畫圖工具-Graphviz和PlantUML

相關文章