使用invokeAll还是submit - java Executor服务

yyhrrdl8  于 2023-05-05  发布在  Java
关注(0)|答案(3)|浏览(316)

我有一个场景,我必须为同一个可调用对象异步执行5个线程。据我所知,有两种选择:
1)使用submit(Callable)

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

2)invokeAll(Callable的集合)

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

1.首选的方式应该是什么?
1.与另一个相比,它们中的任何一个是否有任何缺点或性能影响?

jvlzgdj9

jvlzgdj91#

根据应用要求,它们中的任一个是优选的。

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

用例:如果您不想在任务提交()到ExecutorService之后等待,则首选Option 1
它主要用于异步请求提交。对于非关键用例,我更倾向于选择

  • 示例:* 假设您已预订机票(例如机票)。您希望记录请求和响应以进行内务处理。记录可以在文件或数据库中。在此用例中,日志记录对于向用户发送支付响应并不重要。将日志记录请求提交给ExecutorService,该活动将被异步处理。
    选项二:您正在等待已提交给ExecutorService的所有任务完成。

示例:假设您开发了一个多人游戏。10个玩家是游戏的一部分。5名球员输了钱。在下一场比赛开始之前,您已经为这5名球员提供了重新填充余额的选项。现在开始下一场比赛,你必须等待所有玩家重新填写请求的结果。只有你可以开始下一场比赛。

  • 与另一个相比,它们中的任何一个是否有任何缺点或性能影响?*

这取决于业务用例,如上所述。两者各有优缺点。
还有一件重要的事无论您喜欢什么选项,FutureTask都会在任务执行期间吞下异常。你得小心点看看这个SE问题:处理ThreadPoolExecutor的异常
在Java 8中,你有更多的选择:ExecutorCompletionService
使用提供的Executor执行任务的***CompletionService***。这个类安排提交的任务在完成时被放置在一个可以使用take访问的队列中。该类足够轻量级,适合在处理任务组时暂时使用。
看看相关的SE问题:ExecutorCompletionService? Why do need one if we have invokeAll?

a6b3iqyw

a6b3iqyw2#

编辑:

它们之间其实是有区别的。由于某种原因,invokeAll()将为每个生成的future调用get()。因此,它将等待任务完成,这就是为什么它可能抛出InterruptedException(而submit()什么也不抛出)。
这是invokeAll()方法的Javadoc:
执行给定的任务,返回一个Future列表,其中包含它们的状态和结果当所有任务完成时。
所以,这两种策略基本上做的是相同的,但是如果你调用invokeAll(),你将被阻塞,直到所有任务完成。

原始(不完整)答案:

invokeAll()方法正是针对这种情况而存在的。你一定要用它。
不过,您并不需要示例化List

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

这应该足够了,它看起来比第一种选择更干净。

velaa5lx

velaa5lx3#

假设你有一个任务,其结果取决于独立可执行任务的数量。但是对于完成初始任务来说,你只有有限的时间。就像一个API调用。
因此,例如,您有100毫秒的顶级任务来完成,还有10个相关任务。如果你在这里使用一个submit,代码看起来会是什么样子。

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);
}

因此,如果每个子任务花费50ms来完成,则上面的代码将花费50ms。但是,如果每个子任务花费1000ms来完成,则以上将花费100 * 10 = 1000ms或1s。这使得难以计算所有子任务的总时间小于100ms。
invokeAll方法在这种情况下可以帮助我们

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

这样,即使单个子任务花费的时间超过100ms,最大花费时间也仅为100ms。

相关问题