是否使用 invokeAll 或 submit - java Executor 服务

Whether to use invokeAll or submit - java Executor service

我有一个场景,我必须为同一个可调用对象异步执行 5 个线程。据我了解,有两种选择:

1) 使用提交(可调用)

ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<String>> futures = new ArrayList<>();
for(Callable callableItem: myCallableList){
    futures.add(executorService.submit(callableItem));
}

2) 使用 invokeAll(Collections of Callable)

ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<String>> futures = executorService.invokeAll(myCallableList));
  1. 首选方式应该是什么?
  2. 与另一个相比,它们中的任何一个是否有任何缺点或性能影响?

编辑:

它们之间其实是有区别的。出于某种原因,invokeAll() 将为每个 future 生成调用 get()。因此,它将等待任务完成,这就是为什么它可能会抛出 InterruptedException(而 submit() 什么都不抛出)。

这是 invokeAll() 方法的 Javadoc:

Executes the given tasks, returning a list of Futures holding their status and results when all complete.

所以,这两种策略基本上都是一样的,但是如果你调用 invokeAll() 你会被阻塞,直到所有任务都完成。


原始(不完整)答案:

invokeAll() 方法正是用于此类情况。你绝对应该使用它。

你真的不需要实例化 List,但是:

ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<String>> futures = executorService.invokeAll(myCallableList));

这应该足够了,它看起来比第一个替代方案干净多了。

选项 1:您正在将任务提交给 ExecutorService,并且您没有等待已提交给 ExecutorService 的所有任务完成]

选项 2 : 您正在等待已提交给 ExecutorService.

的所有任务完成

What should be the preferred way?

根据应用要求,首选其中一个。

  1. 如果你不想在任务 submit() 到 ExecutorService 之后等待,更喜欢 Option 1.
  2. 如果需要等待提交给ExecutorService的所有任务完成,首选Option 2

Is there any disadvantage or performance impact in any of them compared to the other one?

如果您的应用程序需要选项 2,与选项 1 不同,您必须等待提交给 ExecutorService 的所有任务完成。性能不是比较标准,因为两者都是为两个不同的目的而设计的。

还有一件更重要的事情:无论您喜欢什么选项,FutureTask 都会在任务执行期间吞下异常。你必须要小心。看看这个 SE 问题:Handling Exceptions for ThreadPoolExecutor

有了Java 8,你就多了一个选择:ExecutorCompletionService

A CompletionService that uses a supplied Executor to execute tasks. This class arranges that submitted tasks are, upon completion, placed on a queue accessible using take. The class is lightweight enough to be suitable for transient use when processing groups of tasks.

看看相关的SE问题:ExecutorCompletionService? Why do need one if we have invokeAll?

假设您有一个任务,其结果取决于独立可执行任务的数量。但是要完成初始任务,您只有有限的时间。就像它的一个 API 调用。

例如,您有 100 毫秒的时间来完成顶级任务,还有 10 个相关任务。为此,如果您在此处使用提交,代码将是什么样子。

List<Callable> tasks = []// assume contains sub tasks
List<Future> futures = [] 
for(Callable task: tasks) {
   futures.add(service.submit(task));
}

for(Future futute: futures) {
    future.get(100, TimeUnit.MILLISECONDS);
}

因此,如果每个子任务花费 exaclty 50ms 来完成上述代码段将花费 50 ms。 但是,如果每个子任务花费 1000 毫秒来完成,则上述任务将花费 100 * 10 = 1000 毫秒或 1 秒。这使得计算所有子任务的总时间小于 100 毫秒变得困难。

invokeAll 方法在这种情况下帮助我们

List<Futures> futures = service.invokeall(tasks, 100, TimeUnit.MILLISECONDS)
for(Future future: futures) {
   if(!future.isCancelled()) {
       results.add(future.get());
   }
}

这样即使单个子任务花费的时间超过 100 毫秒,它所花费的最长时间也只有 100 毫秒。