Java并行流与ExecutorService的性能

时间:2018-09-12 03:37:06

标签: java parallel-processing java-stream java-threads

假设我们有一个列表,想要选择所有满足属性的元素(比如说一些函数f)。有3种方式可以并行执行此过程。

一个:

listA.parallelStream.filter(element -> f(element))..collect(Collectors.toList());

两个:

listA.parallelStream.collect(Collectors.partitioningBy(element -> f(element))).get(true);

三个:

ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
//separate the listA into several batches
for each batch {
     Future<List<T>> result = executorService.submit(() -> {
          // test the elements in this batch and return the validate element list
     });
}
//merge the results from different threads.

假设测试功能是CPU密集型任务。我想知道哪种方法更有效。非常感谢。

1 个答案:

答案 0 :(得分:2)

使用.filter(element -> f(element)).collect(Collectors.toList())时,它将匹配的元素收集到List中,而.collect(Collectors.partitioningBy(element -> f(element)))会将 all 元素收集到两个列表中的任意一个中,您只需删除其中之一,然后仅通过.get(true)检索匹配项列表即可。

很明显,第二个变体在最佳情况下只能与第一个相提并论。,即无论是否所有元素都与谓词匹配,或者当JVM的优化器能够删除冗余元素时,工作。在最坏的情况下,例如当没有元素匹配时,第二个变体收集所有元素的列表,然后将其放下,而第一个变体将不收集任何元素。

第三个变体不可比,因为您没有显示实际的实现,而只是一个草图。将假设的实现与实际的实现进行比较是没有意义的。您描述的逻辑与并行流实现的逻辑相同。因此,您只是在重新发明轮子。您可能会做一些比参考实现更好的事情,或者只是针对您的特定任务做得更好,但是您可能会忽略Stream API实现者在持续了几年的开发过程中已经考虑过的事情。 >

因此,我不会在您的第三个变体上下任何赌注。如果我们加上时间,则需要完成第三个变体的实施,这将永远比仅使用其他变体中的任何一个更有效率。

因此,第一个变体是最有效的变体,尤其是它也是最简单,最易读的,直接表达您意图的变体。