平时工作中因为用到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的主要执行流程图加深理解: