Java-线程池ThreadPoolExecutor之invokeAll()源码解读

平时工作中因为用到ExecutorService的频率略多,而在这之中invokeAll方法又是常用方法。

invokeAll的作用是:等待所有的任务执行完成后统一返回。

关于线程池执行任务后等待任务结束后统一返回的方式,还可以使用shutdown()、awaitTermination()方法来实现。有兴趣的可以看这这篇文章的介绍:Java线程池shutdown()、awaitTermination() 、shutdownNow() 方法的使用

这里与大家分享的是:如果executorService是公共线程池慎用,如果这时候有另外一个请求也不断地往线程池里不断地方任务,这时候这个请求是不是就一直不停的阻塞了。

因此这里详细解读下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);
        }
    }

这里我们关注第一个循环:

       for (Callable<T> t : tasks) {
                RunnableFuture<T> f = newTaskFor(t);
                futures.add(f);
                execute(f);
       }

首先,invokeAll方法会新建一个ArrayList然后foreach循环tasks将每一个tasks add进这个ArrayList。因此如果需要将各个线程的结果按照一定顺序获取,那么可以使用有固定排序的数据结构比如ArrayList来存储每一个task,当方法调用完之后每一个task的结果会按tasks的顺序返回。

对于上面代码第二行RunnableFuture f = newTaskFor(t);来说,它的实际代码如下:


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

也就是说实际上创建了一个FutureTask。FutureTask实现了RunnableFuture接口:

public class FutureTask<V> implements RunnableFuture<V> {

而RunnableFuture接口实际上继承了Runnable以及Future:

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

再来看下Runnable接口对于run方法的描述:

public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

简单来说就是当一个对象实现了Runnable接口并且该对象用于创建一个线程,当线程开启时就会调用run方法。这样一来我们只需要看下FutureTask的run方法就知道其原理了:

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

对于这个run方法我们可以把注意力集中在两行代码上:

result = c.call();
set(result);

第一行代码首先调用了Callable对象的call方法并将该方法的返回值赋值给result,然后通过set方法将返回值绑定给当前的FutureTask对象。

/**
     * Sets the result of this future to the given value unless
     * this future has already been set or has been cancelled.
     *
     * <p>This method is invoked internally by the {@link #run} method
     * upon successful completion of the computation.
     *
     * @param v the value
     */
    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }
    /** The result to return or exception to throw from get() */
    private Object outcome; // non-volatile, protected by state reads/writes

这样一来tasks就都执行了,现在再往下看invokeAll方法:

      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;

这里invokeAll方法会再次循环每一个Future对象以确保所有的Future都已获取了他们的结果:

    /**
     * Waits if necessary for the computation to complete, and then
     * retrieves its result.
     *
     * @return the computed result
     * @throws CancellationException if the computation was cancelled
     * @throws ExecutionException if the computation threw an
     * exception
     * @throws InterruptedException if the current thread was interrupted
     * while waiting
     */
    V get() throws InterruptedException, ExecutionException;

这里通过Future的get方法来实现,get方法会等待线程将结果计算好并获取返回的计算结果,get方法视情况也会抛出线程中断的InterruptedException或者ExecutionException 异常。

最后如果有tasks在执行过程中或者在等待中抛出中断异常,则invokeAll方法会在最后将每一个task关闭以确保资源的释放:

  if (!done)
      for (int i = 0, size = futures.size(); i < size; i++)
          futures.get(i).cancel(true);

最后画一幅invokeAll的主要执行流程图加深理解:
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值