ExecutorCompletionService?如果我们有invokeAll,为什么需要一个?

时间:2022-11-01 22:24:52

If we use an ExecutorCompletionService we can submit a series of tasks as Callables and get the result interacting with the CompletionService as a queue.

如果我们使用ExecutorCompletionService,我们可以将一系列任务作为Callables提交,并将结果作为队列与CompletionService进行交互。

But there is also the invokeAll of ExecutorService that accepts a Collection of tasks and we get a list of Future to retrieve the results.

但是还有一个ExecutorService的invokeAll接受一个任务集合,我们得到一个Future列表来检索结果。

As far as I can tell, there is no benefit in using one or over the other (except that we avoid a for loop using an invokeAll that we would have to submit the tasks to the CompletionService) and essentially they are the same idea with a slight difference.

据我所知,使用一个或另一个没有任何好处(除了我们避免使用invokeAll的for循环,我们必须将任务提交给CompletionService)并且基本上它们与细微差别。

So why are there 2 different ways to submit a series of tasks? Am I correct that performance wise they are equivalent? Is there a case that one is more suitable than the other? I can't think of one.

那么为什么有两种不同的方式来提交一系列任务呢?我是否认为表现明智,他们是相同的?是否有一个比另一个更合适的情况?我想不出一个。

4 个解决方案

#1


62  

Using a ExecutorCompletionService.poll/take, you are receiving the Futures as they finish, in completion order (more or less). Using ExecutorService.invokeAll, you do not have this power; you either block until are all completed, or you specify a timeout after which the incomplete are cancelled.

使用ExecutorCompletionService.poll / take,您将按完成顺序(或多或少)收到期货。使用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;
  }
}

Now, below I will demonstrate how invokeAll works:

现在,下面我将演示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();

This produces the following output:

这会产生以下输出:

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

Using CompletionService, we see a different output:

使用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) { }

This produces the following output:

这会产生以下输出:

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

Note the times are relative to program start, not the previous message.

请注意,时间与程序启动有关,而不是之前的消息。


You can find full code on both here.

你可以在这里找到完整的代码。

#2


17  

By using an ExecutorCompletionService, you can get immediately notified when each of your jobs completes. In comparison, ExecutorService.invokeAll(...) waits for all of your jobs to complete before returning the collection of Futures:

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

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

Instead, when you use a ExecutorCompletionService, you will be able to get the jobs as soon as each of them completes which allows you to (for example) send them on for processing into another thread pool, log results, etc..

相反,当您使用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));
}

#3


2  

I haven't ever actually used ExecutorCompletionService, but I think the case where this could be more useful than "normal" ExecutorService would be when you want to receive the Futures of completed tasks in completion order. With invokeAll, you just get a list that can contain a mix of incomplete and completed tasks at any given time.

我还没有真正使用过ExecutorCompletionService,但我认为这可能比“普通”ExecutorService更有用的情况是当你想要按完成顺序接收完成任务的Futures时。使用invokeAll,您只需获得一个列表,该列表可以包含任何给定时间的未完成和已完成任务的混合。

#4


1  

Comparing by Considering only the Order of Results:

仅考虑结果顺序进行比较:

When we use CompletionService whenever a job submitted is finished the result will be pushed to the queue (Completion Order). Then the order of the submitted jobs and the returned results is no more same. So if you are concerned about the order which tasks got executed use CompletionService

当我们在提交的作业完成后使用CompletionService时,结果将被推送到队列(完成顺序)。然后,提交的作业的顺序和返回的结果不再相同。因此,如果您担心执行任务的顺序,请使用CompletionService

Where As invokeAll returns a list of Futures representing the tasks, in the same sequential order as produced by the iterator for the given task list, each of which has completed.

其中As invokeAll返回表示任务的Futures列表,其顺序与给定任务列表的迭代器生成的顺序相同,每个已完成。

#1


62  

Using a ExecutorCompletionService.poll/take, you are receiving the Futures as they finish, in completion order (more or less). Using ExecutorService.invokeAll, you do not have this power; you either block until are all completed, or you specify a timeout after which the incomplete are cancelled.

使用ExecutorCompletionService.poll / take,您将按完成顺序(或多或少)收到期货。使用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;
  }
}

Now, below I will demonstrate how invokeAll works:

现在,下面我将演示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();

This produces the following output:

这会产生以下输出:

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

Using CompletionService, we see a different output:

使用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) { }

This produces the following output:

这会产生以下输出:

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

Note the times are relative to program start, not the previous message.

请注意,时间与程序启动有关,而不是之前的消息。


You can find full code on both here.

你可以在这里找到完整的代码。

#2


17  

By using an ExecutorCompletionService, you can get immediately notified when each of your jobs completes. In comparison, ExecutorService.invokeAll(...) waits for all of your jobs to complete before returning the collection of Futures:

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

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

Instead, when you use a ExecutorCompletionService, you will be able to get the jobs as soon as each of them completes which allows you to (for example) send them on for processing into another thread pool, log results, etc..

相反,当您使用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));
}

#3


2  

I haven't ever actually used ExecutorCompletionService, but I think the case where this could be more useful than "normal" ExecutorService would be when you want to receive the Futures of completed tasks in completion order. With invokeAll, you just get a list that can contain a mix of incomplete and completed tasks at any given time.

我还没有真正使用过ExecutorCompletionService,但我认为这可能比“普通”ExecutorService更有用的情况是当你想要按完成顺序接收完成任务的Futures时。使用invokeAll,您只需获得一个列表,该列表可以包含任何给定时间的未完成和已完成任务的混合。

#4


1  

Comparing by Considering only the Order of Results:

仅考虑结果顺序进行比较:

When we use CompletionService whenever a job submitted is finished the result will be pushed to the queue (Completion Order). Then the order of the submitted jobs and the returned results is no more same. So if you are concerned about the order which tasks got executed use CompletionService

当我们在提交的作业完成后使用CompletionService时,结果将被推送到队列(完成顺序)。然后,提交的作业的顺序和返回的结果不再相同。因此,如果您担心执行任务的顺序,请使用CompletionService

Where As invokeAll returns a list of Futures representing the tasks, in the same sequential order as produced by the iterator for the given task list, each of which has completed.

其中As invokeAll返回表示任务的Futures列表,其顺序与给定任务列表的迭代器生成的顺序相同,每个已完成。