是否使用invokeAll或submit – java Executor服务
我有一个场景,我必须为同一个callable异步执行5个线程。 据我了解,有两种选择:
1)使用submit(Callable)
ExecutorService executorService = Executors.newFixedThreadPool(5); List<Future> futures = new ArrayList(); for(Callable callableItem: myCallableList){ futures.add(executorService.submit(callableItem)); }
2)使用invokeAll(Callable集合)
ExecutorService executorService = Executors.newFixedThreadPool(5); List<Future> futures = executorService.invokeAll(myCallableList));
- 应该是什么首选方式?
- 与其他产品相比,它们中的任何产品是否存在任何劣势或性能影响?
选项1 :您正在向ExecutorService
提交任务,而您不是在等待已提交给ExecutorService
的所有任务的完成
选项2 :您正在等待已提交给ExecutorService
的所有任务的完成。
应该是什么首选方式?
根据应用要求,它们中的任何一个都是首选。
- 如果您在任务submit()之后不等待
ExecutorService
,则更喜欢Option 1
。 - 如果您需要等待已提交给
ExecutorService
的所有任务的完成,则更喜欢Option 2
。
与其他产品相比,它们中的任何产品是否存在任何劣势或性能影响?
如果您的应用程序需要选项2,则必须等待所有提交的任务完成到ExecutorService
与选项1不同。性能不是比较的标准,因为它们都是为两个不同的目的而设计的。
还有一件更重要的事情:无论您喜欢什么选项, FutureTask
在任务执行期间吞下Exceptions。 你必须要小心。 看看这个SE问题: 处理ThreadPoolExecutor的exception
使用Java 8,您还有一个选项: ExecutorCompletionService
一个CompletionService ,它使用提供的Executor来执行任务。 该类安排提交的任务在完成后放置在可使用take访问的队列中。 该类足够轻巧,适合在处理任务组时进行瞬态使用。
看看相关的SE问题: ExecutorCompletionService? 如果我们有invokeAll,为什么需要一个?
编辑:
它们之间实际上存在差异。 出于某种原因, invokeAll()
将为每个生成的future
调用get()
。 因此,它将等待任务完成,这就是为什么它可能抛出InterruptedException
(而submit()
抛出任何东西)。
这是invokeAll()
方法的Javadoc:
执行给定的任务,返回完成所有状态和结果的Futures列表。
因此,两种策略基本上都是一样的,但如果你调用invokeAll()
你将被阻止,直到所有任务完成。
原始(不完整)答案:
invokeAll()
方法完全适用于这些情况。 你一定要用它。
但是,您并不需要实例化该List
:
ExecutorService executorService = Executors.newFixedThreadPool(5); List> futures = executorService.invokeAll(myCallableList));
这应该足够了,它看起来比第一种选择更清洁。
假设您有一个任务,其结果取决于独立可执行任务的数量。 但是对于完成初始任务,您只有有限的时间。 就像它的API调用一样。
因此,例如,您需要100毫秒才能完成顶级任务,并且还有10个相关任务。 为此,如果您在这里使用提交代码将如何。
List tasks = []// assume contains sub tasks List futures = [] for(Callable task: tasks) { futures.add(service.submit(task)); } for(Future futute: futures) { future.get(100, TimeUnit.MILLISECONDS); }
因此,如果每个子任务花费50ms来完成上述代码,则需要50 ms。 但是如果每个子任务花费1000毫秒来完成上述将花费100 * 10 = 1000毫秒或1秒。 这使得难以计算所有子任务的总时间小于100ms。
invokeAll方法在这种情况下帮助我们
List futures = service.invokeall(tasks, 100, TimeUnit.MILLISECONDS) for(Future future: futures) { if(!future.isCancelled()) { results.add(future.get()); } }
这样,即使个别子任务占用超过100毫秒,它所需的最长时间也只需100毫秒。