聊聊高並發(四十一)解析java.util.concurrent各個組件(十七) 任務的非同步執行和狀態控制

來源:互聯網
上載者:User

標籤:executor   future   futuretask   callable   

聊聊高並發(三十九)解析java.util.concurrent各個組件(十五) 理解ExecutorService介面的設計這篇說了ExecutorService介面擴充了Executor介面,在執行任務的基礎上,提供了執行架構生命週期的管理,任務的非同步執行,批量任務的執行的能力。AbstractExecutorService抽象類別實現了ExecutorService介面,提供了任務非同步執行和批量執行的預設實現。這篇說說任務的非同步執行和狀態控制

說明一點,使用Executor架構執行任務的方式基本都是非同步執行的,交給線程池的線程來執行。ExecutorService在任務非同步執行的基礎上,通過Future介面來對非同步執行的任務進行狀態控制。


submit方法可以返回Future對象來對非同步執行任務進行控制。submit方法有三種調用方式,傳遞Runnable, Runnable和result,Callable。

 public Future<?> submit(Runnable task) {        if (task == null) throw new NullPointerException();        RunnableFuture<Void> ftask = newTaskFor(task, null);        execute(ftask);        return ftask;    }    public <T> Future<T> submit(Runnable task, T result) {        if (task == null) throw new NullPointerException();        RunnableFuture<T> ftask = newTaskFor(task, result);        execute(ftask);        return ftask;    }    public <T> Future<T> submit(Callable<T> task) {        if (task == null) throw new NullPointerException();        RunnableFuture<T> ftask = newTaskFor(task);        execute(ftask);        return ftask;    }

從submit的實現可以看到,都是使用了newTaskFor方法進行了介面的適配,返回一個RunnableFuture類型

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {        return new FutureTask<T>(runnable, value);    }       protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {        return new FutureTask<T>(callable);    }

來看一下對非同步執行的任務進行控制的Future介面相關的類

1. Ruuable表示可被Thread執行的任務,它的run方法沒有返回值,並且沒有顯式的異常

2. Callable表示可以調用的任務,它的call方法可以有返回值,可以拋出顯式的異常

3. Future介面是對非同步執行的任務的控制,包括取消任務,判斷任務狀態,擷取任務結果等操作

4. RunnableFuture是對Runnable介面和Future介面的適配,表示可以被控制狀態的Runnable

5. RunnableAdapter是對Runnable和Callalbe的適配,實現了Callable介面,並彙總了Runnable對象

6. FutureTask實現了RunnableFuture介面,通過RunnableAdapter對傳入的Callable和Runnable參數進行統一處理



public interface Runnable {    public abstract void run();}public interface Callable<V> {    V call() throws Exception;}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;        }    }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;}public interface RunnableFuture<V> extends Runnable, Future<V> {    void run();}

在ExecutorService的submit方法中可以看到最終被執行的任務是封裝成了RunnableFuture類型,它既是Runnable可以被Executor執行,又是Future,可以控制非同步執行的Runnable的狀態。

重點來看一下RunnableFuture的實作類別FutureTask,非同步執行的任務的狀態控制都是在這個類中實現的。

FutureTask的主要屬性

1.  private volatile int state; volatile類型的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;


2. private Callable<V> callable; 可以獲得計算結果的callable對象,封裝了傳入的任務

3. private Object outcome; 任務執行的結果,可以是正常計算得到的結果,也可以是要返回的異常

4. private volatile Thread runner; 執行任務的線程

5. private volatile WaitNode waiters; 等待的線程鏈表

FutureTask對state, runner, waiters這3個需要原子操作的對象,沒有使用AtomicXXX原子變數,而是使用了Unsafe對象來直接操作記憶體進行原子操作。關於Unsafe對象請參考這篇聊聊高並發(十七)解析java.util.concurrent各個組件(一) 瞭解sun.misc.Unsafe類

 // 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"));            runnerOffset = UNSAFE.objectFieldOffset                (k.getDeclaredField("runner"));            waitersOffset = UNSAFE.objectFieldOffset                (k.getDeclaredField("waiters"));        } catch (Exception e) {            throw new Error(e);        }    }

來看看FutureTask的主要方法

建構函式支援Callable類型和Runnable類型,當使用Runnable類型的參數時,需要傳入result作為Callable的計算結果,利用RunnableAdapter進行從Runnable到Callable的適配。FutureTask的初始狀態是NEW

 public FutureTask(Callable<V> callable) {        if (callable == null)            throw new NullPointerException();        this.callable = callable;        this.state = NEW;       // ensure visibility of callable    } public FutureTask(Runnable runnable, V result) {        this.callable = Executors.callable(runnable, result);        this.state = NEW;       // ensure visibility of callable    }// Executors.callable() public static <T> Callable<T> callable(Runnable task, T result) {        if (task == null)            throw new NullPointerException();        return new RunnableAdapter<T>(task, result);    }

run方法會被Executor的背景工作執行緒調用,因為FutureTask是作為Runnable對象傳遞給Executor的execute方法的。

1. 把當前線程設定成FutureTask的runner。

2. 執行傳入的Callable的call方法,並把結果傳給result對象。如果call方法產生異常,就調用setException方法把異常對象傳遞給outcome屬性,並設定FutureTask的相應狀態,先設定成COMPLETING,再設定成EXCEPTIONAL

3. 如果call方法正常執行完成,調用set()方法把結果傳遞給outcome屬性,並設定FutureTask的相應狀態,先設定成COMPLETING,再設定成NORMAL

4. 調用finishCompletion方法來喚醒在get()方法中阻塞的線程

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);        }    } protected void setException(Throwable t) {        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {            outcome = t;            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state            finishCompletion();        }    }protected void set(V v) {        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {            outcome = v;            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state            finishCompletion();        }    }

cancel方法會取消執行的任務。當state為NEW的時候才能被取消。如果取消任務時要中斷任務,會先把FutureTask狀態設定為INTERRUPTING,然後調用執行任務的線程的interrupt()方法去中斷任務,然後把狀態設定成INTERRUPTED。如果取消任務時不中斷任務,直接把FutureTask狀態設定成CANCELLED。最後調用finishCompletiong方法來喚醒和刪除在get方法中等待的線程

  public boolean cancel(boolean mayInterruptIfRunning) {        if (state != NEW)            return false;        if (mayInterruptIfRunning) {            if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING))                return false;            Thread t = runner;            if (t != null)                t.interrupt();            UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state        }        else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED))            return false;        finishCompletion();        return true;    } 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    }// 鉤子方法protected void done() { }

get()和get(long timeout, TimeUnit unit)方法會嘗試擷取FutureTask的運行結果。前者會一直等待,直到FutureTask的狀態大於COMPLETING,即call執行結束,然後report()方法返回結果。後者會等待timeout時間,如果timeout就拋出TimeoutException,否則report()方法返回結果

 public V get() throws InterruptedException, ExecutionException {        int s = state;        if (s <= COMPLETING)            s = awaitDone(false, 0L);        return report(s);    }    /**     * @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);    }

awaitDone方法利用了LockSupport對象來實現條件隊列操作,LockSupport.park()  LockSupport.unpark()方法相當於即Object.wait(),Object.notify()方法。

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

report方法處理FutureTask的返回值,如果正常執行,就返回正常結果,如果是取消或者中斷,就拋出CancellationException,如果執行過程中發生了異常,就用ExecutionException來封裝拋出的異常,並拋出ExecutionException

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對象,這兩個線程之間可以通過FutureTask的狀態相互作用




著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

聊聊高並發(四十一)解析java.util.concurrent各個組件(十七) 任務的非同步執行和狀態控制

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.