我有一个场景,我必须为同一个可调用对象异步执行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.与另一个相比,它们中的任何一个是否有任何缺点或性能影响?
3条答案
按热度按时间jvlzgdj91#
根据应用要求,它们中的任一个是优选的。
选项1:您正在将任务提交到
ExecutorService
,并且没有等待已提交到ExecutorService
的所有任务完成用例:如果您不想在任务提交()到
ExecutorService
之后等待,则首选Option 1
。它主要用于异步请求提交。对于非关键用例,我更倾向于选择
选项二:您正在等待已提交给
ExecutorService
的所有任务完成。示例:假设您开发了一个多人游戏。10个玩家是游戏的一部分。5名球员输了钱。在下一场比赛开始之前,您已经为这5名球员提供了重新填充余额的选项。现在开始下一场比赛,你必须等待所有玩家重新填写请求的结果。只有你可以开始下一场比赛。
这取决于业务用例,如上所述。两者各有优缺点。
还有一件重要的事无论您喜欢什么选项,
FutureTask
都会在任务执行期间吞下异常。你得小心点看看这个SE问题:处理ThreadPoolExecutor的异常在Java 8中,你有更多的选择:ExecutorCompletionService
使用提供的Executor执行任务的***CompletionService***。这个类安排提交的任务在完成时被放置在一个可以使用take访问的队列中。该类足够轻量级,适合在处理任务组时暂时使用。
看看相关的SE问题:ExecutorCompletionService? Why do need one if we have invokeAll?
a6b3iqyw2#
编辑:
它们之间其实是有区别的。由于某种原因,
invokeAll()
将为每个生成的future
调用get()
。因此,它将等待任务完成,这就是为什么它可能抛出InterruptedException
(而submit()
什么也不抛出)。这是
invokeAll()
方法的Javadoc:执行给定的任务,返回一个Future列表,其中包含它们的状态和结果当所有任务完成时。
所以,这两种策略基本上做的是相同的,但是如果你调用
invokeAll()
,你将被阻塞,直到所有任务完成。原始(不完整)答案:
invokeAll()
方法正是针对这种情况而存在的。你一定要用它。不过,您并不需要示例化
List
:这应该足够了,它看起来比第一种选择更干净。
velaa5lx3#
假设你有一个任务,其结果取决于独立可执行任务的数量。但是对于完成初始任务来说,你只有有限的时间。就像一个API调用。
因此,例如,您有100毫秒的顶级任务来完成,还有10个相关任务。如果你在这里使用一个submit,代码看起来会是什么样子。
因此,如果每个子任务花费50ms来完成,则上面的代码将花费50ms。但是,如果每个子任务花费1000ms来完成,则以上将花费100 * 10 = 1000ms或1s。这使得难以计算所有子任务的总时间小于100ms。
invokeAll方法在这种情况下可以帮助我们
这样,即使单个子任务花费的时间超过100ms,最大花费时间也仅为100ms。