ExecutorService.invokeAny()和ExecutorService.invokeAll()的使用方式,可以参考我之前的一篇文章。之前的博客按照test and learn的学习方式(有点类似于测试驱动开发),构造了我能想到的几种场景来进行测试,通过分析测试结果,也得出了一些结论,学会了这2个API的使用。本文主要是阅读下J.U.C源码,一则验证下之前的一些推论,二则学习下源码中的一些技巧,这是通过执行测试用例看不到的。
这2个API的实现逻辑是在AbstractExecutorService中
public abstract class AbstractExecutorService implements ExecutorService{
}
首先分析下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();
List<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;
long lastTime = (timed)? System.nanoTime() : 0;
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();
long now = System.nanoTime();
nanos -= now - lastTime;