ExecutorCompletionService? 为什么需要一个,如果我们的invokeAl

2019-06-26 21:00发布

如果我们用一个ExecutorCompletionService我们可以提交一系列任务的Callable S和得到的结果与交互CompletionServicequeue

但也有对invokeAllExecutorService一个接受Collection任务,我们得到的名单Future检索结果。

据我所知,有在使用一个以上的其他(但我们避免毫无益处for循环使用invokeAll ,我们将不得不submit任务的CompletionService ),基本上他们是有同样的想法细微的差别。

那么,为什么会有两种不同的方式提交一系列的任务? 难道我纠正性能明智的,他们是等价? 是否有一个比其它更适合的情况下? 我想不出之一。

Answer 1:

使用ExecutorCompletionService.poll/take ,您收到的Future S作为他们完成,在完成顺序(或多或少)。 使用ExecutorService.invokeAll ,你没有这个权力; 你要么块,直到全部完成后,或您指定在此之后,不完全被取消超时。


static class SleepingCallable implements Callable<String> {

  final String name;
  final long period;

  SleepingCallable(final String name, final long period) {
    this.name = name;
    this.period = period;
  }

  public String call() {
    try {
      Thread.sleep(period);
    } catch (InterruptedException ex) { }
    return name;
  }
}

现在,下面我将演示如何invokeAll工作:

final ExecutorService pool = Executors.newFixedThreadPool(2);
final List<? extends Callable<String>> callables = Arrays.asList(
    new SleepingCallable("quick", 500),
    new SleepingCallable("slow", 5000));
try {
  for (final Future<String> future : pool.invokeAll(callables)) {
    System.out.println(future.get());
  }
} catch (ExecutionException | InterruptedException ex) { }
pool.shutdown();

这将产生以下输出:

C:\dev\scrap>java CompletionExample
... after 5 s ...
quick
slow

使用CompletionService ,我们看到了一个不同的输出:

final ExecutorService pool = Executors.newFixedThreadPool(2);
final CompletionService<String> service = new ExecutorCompletionService<String>(pool);
final List<? extends Callable<String>> callables = Arrays.asList(
    new SleepingCallable("slow", 5000),
    new SleepingCallable("quick", 500));
for (final Callable<String> callable : callables) {
  service.submit(callable);
}
pool.shutdown();
try {
  while (!pool.isTerminated()) {
    final Future<String> future = service.take();
    System.out.println(future.get());
  }
} catch (ExecutionException | InterruptedException ex) { }

这将产生以下输出:

C:\dev\scrap>java CompletionExample
... after 500 ms ...
quick
... after 5 s ...
slow

注意时间都是相对于程序开始,而不是以前的消息。


您可以同时找到完整的代码在这里 。



Answer 2:

通过使用ExecutorCompletionService ,您可以立即得到每个作业完成时通知。 相比之下, ExecutorService.invokeAll(...)等待您的所有任务返回的集合之前完成的Future S:

// this waits until _all_ of the jobs complete
List<Future<Object>> futures = threadPool.invokeAll(...);

相反,当你使用一个ExecutorCompletionService ,您将能够尽快得到工作,因为每个人完成,它允许你(例如)送他们加工成另一个线程池,日志结果等。

ExecutorService threadPool = Executors.newFixedThreadPool(2);
ExecutorCompletionService<Result> compService
      = new ExecutorCompletionService<Result>(threadPool);
for (MyJob job : jobs) {
    compService.submit(job);
}
// shutdown the pool but the jobs submitted continue to run
threadPool.shutdown();
while (!threadPool.isTerminated()) {
    // the take() blocks until any of the jobs complete
    // this joins with the jobs in the order they _finish_
    Future<Result> future = compService.take();
    // this get() won't block
    Result result = future.get();
    // you can then put the result in some other thread pool or something
    // to immediately start processing it
    someOtherThreadPool.submit(new SomeNewJob(result));
}


Answer 3:

我还没有过实际使用ExecutorCompletionService,但我想在那里,当你想收到的已完成任务的期货完成顺序,这可能会比“正常”的ExecutorService更有用的是这种情况。 的invokeAll有了,你只会得到可以在任何给定的时间包含不完整的,已完成任务的混合列表。



Answer 4:

只考虑结果的顺序进行比较:

当我们使用CompletionService只要提交完成结果的工作将被推到队列(完成顺序)。 然后提交的作业,并返回结果的顺序是不一样多。 所以,如果你关心哪些任务得到执行使用该命令CompletionService

凡为invokeAll返回表示由迭代器给定的任务列表,每个已完成生产任务,在同一顺序期货的列表。



文章来源: ExecutorCompletionService? Why do need one if we have invokeAll?