《java.util.concurrent 包源碼閱讀》10 線程池系列之AbstractExecutorService

來源:互聯網
上載者:User

標籤:style   blog   color   java   os   io   for   ar   

AbstractExecutorService對ExecutorService的執行任務類型的方法提供了一個預設實現。這些方法包括submit,invokeAny和InvokeAll。

注意的是來自Executor介面的execute方法是未被實現,execute方法是整個體系的核心,所有的任務都是在這個方法裡被真正執行的,因此該方法的不同實現會帶來不同的執行策略。這個在後面分析ThreadPoolExecutor和ScheduledThreadPoolExecutor就能看出來。

 

首先來看submit方法,它的基本邏輯是這樣的:

1. 產生一個任務類型和Future介面的封裝介面RunnableFuture的對象

2. 執行任務

3. 返回future。

    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(Callable<T> task) {        if (task == null) throw new NullPointerException();        RunnableFuture<T> ftask = newTaskFor(task);        execute(ftask);        return ftask;    }

因為submit支援Callable和Runnable兩種類型的任務,因此newTaskFor方法有兩個重載方法:

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

上一篇文章裡曾經說過Callable和Runnable的區別在於前者帶傳回值,也就是說Callable=Runnable+傳回值。因此java中提供了一種adapter,把Runnable+傳回值轉換成Callable類型。這點可以在newTaskFor中的FutureTask類型的建構函式的代碼中看到:

    public FutureTask(Callable<V> callable) {        if (callable == null)            throw new NullPointerException();        sync = new Sync(callable);    }    public FutureTask(Runnable runnable, V result) {        sync = new Sync(Executors.callable(runnable, result));    }

以下是Executors.callable方法的代碼:

    public static <T> Callable<T> callable(Runnable task, T result) {        if (task == null)            throw new NullPointerException();        return new RunnableAdapter<T>(task, result);    }

那麼RunnableAdapter的代碼就很好理解了,它是一個Callable的實現,call方法的實現就是執行Runnable的run方法,然後返回那個value。

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

接下來先說說較為簡單的invokeAll:

1. 為每個task調用newTaskFor方法產生得到一個既是Task也是Future的封裝類對象的List

2. 迴圈調用execute執行每個任務

3. 再次迴圈調用每個Future的get方法等待每個task執行完成

4. 最後返回Future的list。

    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,                                         long timeout, TimeUnit unit)        throws InterruptedException {        if (tasks == null || unit == null)            throw new NullPointerException();        long nanos = unit.toNanos(timeout);        List<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());        boolean done = false;        try {            // 為每個task產生封裝對象            for (Callable<T> t : tasks)                futures.add(newTaskFor(t));            long lastTime = System.nanoTime();            // 迴圈調用execute執行每個方法            // 這裡因為設定了逾時時間,所以每次執行完成後            // 檢查是否逾時,逾時了就直接返回future集合            Iterator<Future<T>> it = futures.iterator();            while (it.hasNext()) {                execute((Runnable)(it.next()));                long now = System.nanoTime();                nanos -= now - lastTime;                lastTime = now;                if (nanos <= 0)                    return futures;            }            // 等待每個任務執行完成            for (Future<T> f : futures) {                if (!f.isDone()) {                    if (nanos <= 0)                        return futures;                    try {                        f.get(nanos, TimeUnit.NANOSECONDS);                    } catch (CancellationException ignore) {                    } catch (ExecutionException ignore) {                    } catch (TimeoutException toe) {                        return futures;                    }                    long now = System.nanoTime();                    nanos -= now - lastTime;                    lastTime = now;                }            }            done = true;            return futures;        } finally {            if (!done)                for (Future<T> f : futures)                    f.cancel(true);        }    }

最後說說invokeAny,它的痛點在於只要一個任務執行成功就要返回,並且會取消其他任務,也就是說重點在於找到第一個執行成功的任務。

這裡我想到了BlockingQueue,當所有的任務被提交後,任務執行返回的Future會被依次添加到一個BlockingQueue中,然後找到第一個執行成功任務的方法就是從BlockingQueue取出第一個元素,這個就是doInvokeAny方法用到的ExecutorCompletionService的基本原理。

因為兩個invokeAny方法都是調用doInvokeAny方法,下面是doInvokeAny的程式碼分析:

    private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,                            boolean timed, long nanos)        throws InterruptedException, ExecutionException, TimeoutException {        if (tasks == null)            throw new NullPointerException();        int ntasks = tasks.size();        if (ntasks == 0)            throw new IllegalArgumentException();        List<Future<T>> futures= new ArrayList<Future<T>>(ntasks);        // ExecutorCompletionService負責執行任務,後面調用用poll返回第一個執行結果        ExecutorCompletionService<T> ecs =            new ExecutorCompletionService<T>(this);        // 這裡出於效率的考慮,每次提交一個任務之後,就檢查一下有沒有執行完成的任務        try {            ExecutionException ee = null;            long lastTime = timed ? System.nanoTime() : 0;            Iterator<? extends Callable<T>> it = tasks.iterator();            // 先提交一個任務            futures.add(ecs.submit(it.next()));            --ntasks;            int active = 1;            for (;;) {                // 嘗試擷取有沒有執行結果(這個結果是立刻返回的)                Future<T> f = ecs.poll();                // 沒有執行結果                if (f == null) {                    // 如果還有任務沒有被提交執行的,就再提交一個任務                    if (ntasks > 0) {                        --ntasks;                        futures.add(ecs.submit(it.next()));                        ++active;                    }                    // 沒有任務在執行了,而且沒有拿到一個成功的結果。                    else if (active == 0)                        break;                    // 如果設定了逾時情況                    else if (timed) {                        // 等待執行結果直到有結果或者逾時                        f = ecs.poll(nanos, TimeUnit.NANOSECONDS);                        if (f == null)                            throw new TimeoutException();                        // 這裡的更新不可少,因為這個Future可能是執行失敗的情況,那麼還需要再次等待下一個結果,逾時的設定還是需要用到。                        long now = System.nanoTime();                        nanos -= now - lastTime;                        lastTime = now;                    }                    // 沒有設定逾時,並且所有任務都被提交了,則一直等到第一個執行結果出來                    else                        f = ecs.take();                }                // 有返回結果了,嘗試從future中擷取結果,如果失敗了,那麼需要接著等待下一個執行結果                if (f != null) {                    --active;                    try {                        return f.get();                    } catch (ExecutionException eex) {                        ee = eex;                    } catch (RuntimeException rex) {                        ee = new ExecutionException(rex);                    }                }            }            // ExecutorCompletionService執行時發生錯誤返回了全是null的future            if (ee == null)                ee = new ExecutionException();            throw ee;        } finally {            // 嘗試取消所有的任務(對於已經完成的任務沒有影響)            for (Future<T> f : futures)                f.cancel(true);        }    }

 

後面接著分析ThreadPoolExecutor和ScheduledThreadPoolExecutor。

相關文章

聯繫我們

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