AbstractExecutorService源码理解

来源:互联网 发布:java垃圾回收原理 编辑:程序博客网 时间:2024/06/06 00:43

这个类提供 ExecutorService 执行方法的默认实现。此类使用 newTaskFor 返回的 RunnableFuture 实现 submit、invokeAny 和 invokeAll 方法,默认情况下,RunnableFuture 是此包中提供的 FutureTask 类。例如,submit(Runnable) 的实现创建了一个关联 RunnableFuture 类,该类将被执行并返回。子类可以重写 newTaskFor 方法,以返回 FutureTask 之外的 RunnableFuture 实现。

理解源码之前,先来看下这个类:ExecutorCompletionService ,实现了CompletionService接口,这个类将安排那些完成时提交的任务,把它们放置在可使用 take 访问的队列上。该类非常轻便,适合于在执行几组任务时临时使用
它将异步任务的提交与对结果的获取解耦,生产者提交任务,消费者在任务完成后处理,CompletionService可以处理异步I/O任务,执行读操作的任务作为程序或系统的一部分提交,
然后,当完成读操作时,会在程序的不同部分执行其他操作,执行操作的顺序可能与所请求的顺序不同。
通常,CompletionService 依赖于一个单独的 Executor 来实际执行任务,在这种情况下,CompletionService 只管理一个内部完成队列。
这个是里面的核心的部分,维护一个内部的队列:

/**     * FutureTask extension to enqueue upon completion     */    private class QueueingFuture extends FutureTask<Void> {        QueueingFuture(RunnableFuture<V> task) {            super(task, null);            this.task = task;        }        protected void done() { completionQueue.add(task); }        private final Future<V> task;    }    public Future<V> submit(Callable<V> task) {        if (task == null) throw new NullPointerException();        RunnableFuture<V> f = newTaskFor(task);        executor.execute(new QueueingFuture(f));        return f;    }
在submit任务时,使用QueueingFuture包装后提交给线程池执行,其中QueueingFuture继承自FutureTask,重写了done()方法,在执行完成后,调用completionQueue.add(task);将任务加入队列。这样在外部就可以通过poll 或者 take 来获取任务结果了。这种设计算是一种装饰模式的应用了。

我们再回来看AbstractExecutorService,其它方法很简单,之前的博客已将讲过了。主要阅读下invokeAny 和 invokeAll即可。
先看下invokeAny:

/**     * the main mechanics of invokeAny.     */    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();        ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks);        ExecutorCompletionService<T> ecs =            new ExecutorCompletionService<T>(this);        // For efficiency, especially in executors with limited        // parallelism, check to see if previously submitted tasks are        // done before submitting more of them. This interleaving        // plus the exception mechanics account for messiness of main        // loop.        try {            // Record exceptions so that if we fail to obtain any            // result, we can throw the last exception we got.            ExecutionException ee = null;            final long deadline = timed ? System.nanoTime() + nanos : 0L;            Iterator<? extends Callable<T>> it = tasks.iterator();            // Start one task for sure; the rest incrementally            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();                        nanos = deadline - System.nanoTime();                    }                    else                        f = ecs.take();                }                if (f != null) {                    --active;                    try {                        return f.get();                    } catch (ExecutionException eex) {                        ee = eex;                    } catch (RuntimeException rex) {                        ee = new ExecutionException(rex);                    }                }            }            if (ee == null)                ee = new ExecutionException();            throw ee;        } finally {            for (int i = 0, size = futures.size(); i < size; i++)                futures.get(i).cancel(true);        }    }
在做完基本的判空后,首选的处理为:
ExecutorCompletionService<T> ecs = new ExecutorCompletionService<T>(this); 
ExecutorCompletionService 前文已经说明,装饰了AbstractExecutorService,使其持有了返回结果的队列的功能。之后将返回结果Future加入到ArrayList<Future<T>> futures中,这个为了之后的回收使用的。进入循环,
Future<T> f = ecs.poll();
ecs从内部队列中取出一个返回结果, 如果不是null, 那么return f.get(); 返回计算结果。否则,表明当前内部队列为空,还没有Future对象呢,一条条分析:如果ntasks > 0,表明还有未放入ecs线程池的task,执行futures.add(ecs.submit(it.next()));提交到线程池中执行。active == 0,表明已经没有在运行中的task了,跳出循环。如果timed,表明有等待时间,则在nanos的等待时间内取结果。如果不是timed,则ecs.take()阻塞等待去结果。最后在finally中,将之前加入futures中的task取消执行。

再看invokeAll:

public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)        throws InterruptedException {        if (tasks == null)            throw new NullPointerException();        ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());        boolean done = false;        try {            for (Callable<T> t : tasks) {                RunnableFuture<T> f = newTaskFor(t);                futures.add(f);                execute(f);            }            for (int i = 0, size = futures.size(); i < size; i++) {                Future<T> f = futures.get(i);                if (!f.isDone()) {                    try {                        f.get();                    } catch (CancellationException ignore) {                    } catch (ExecutionException ignore) {                    }                }            }            done = true;            return futures;        } finally {            if (!done)                for (int i = 0, size = futures.size(); i < size; i++)                    futures.get(i).cancel(true);        }    }
实例化ArrayList<Future<T>> futures, 作为之后承载返回结果的对象。
for (Callable<T> t : tasks) {                RunnableFuture<T> f = newTaskFor(t);                futures.add(f);                execute(f);            }
遍历tasks,futures.add(f),然后调用execute执行,当然,execute方法由子类实现。
for (int i = 0, size = futures.size(); i < size; i++) {                Future<T> f = futures.get(i);                if (!f.isDone()) {                    try {                        f.get();                    } catch (CancellationException ignore) {                    } catch (ExecutionException ignore) {                    }                }            }
这块作用最关键是f.get(),这个方法会阻塞一直等到计算完成返回结果为止,如果执行出错,这个方法会抛出异常,这里将异常吃掉了,之后外部获取的时候不会抛出任何异常了。最后在finally中,判断如果运行有错的话,将剩余的任务cancel掉。

public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,                                         long timeout, TimeUnit unit)        throws InterruptedException {        if (tasks == null)            throw new NullPointerException();        long nanos = unit.toNanos(timeout);        ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());        boolean done = false;        try {            for (Callable<T> t : tasks)                futures.add(newTaskFor(t));            final long deadline = System.nanoTime() + nanos;            final int size = futures.size();            // Interleave time checks and calls to execute in case            // executor doesn't have any/much parallelism.            for (int i = 0; i < size; i++) {                execute((Runnable)futures.get(i));                nanos = deadline - System.nanoTime();                if (nanos <= 0L)                    return futures;            }            for (int i = 0; i < size; i++) {                Future<T> f = futures.get(i);                if (!f.isDone()) {                    if (nanos <= 0L)                        return futures;                    try {                        f.get(nanos, TimeUnit.NANOSECONDS);                    } catch (CancellationException ignore) {                    } catch (ExecutionException ignore) {                    } catch (TimeoutException toe) {                        return futures;                    }                    nanos = deadline - System.nanoTime();                }            }            done = true;            return futures;        } finally {            if (!done)                for (int i = 0, size = futures.size(); i < size; i++)                    futures.get(i).cancel(true);        }    }
前面的部分和之前的无超时的 invokeAll 方法逻辑相同,主要看下:
for (int i = 0; i < size; i++) {                Future<T> f = futures.get(i);                if (!f.isDone()) {                    if (nanos <= 0L)                        return futures;                    try {                        f.get(nanos, TimeUnit.NANOSECONDS);                    } catch (CancellationException ignore) {                    } catch (ExecutionException ignore) {                    } catch (TimeoutException toe) {                        return futures;                    }                    nanos = deadline - System.nanoTime();                }            }
从futures中取出一个future,然后调用 f.get(nanos, TimeUnit.NANOSECONDS)等待计算结果, 完成后会更新计算超时时间,nanos = deadline - System.nanoTime();这句表明这个方法的超时时间是所有tasks共用的超时时间,看代码才发现这个知识点。
0 0
原创粉丝点击