1.概述
Java 8引入了Streams的概念,作为对数据执行批量操作的有效方式。并行Streams可以在支持并发的环境中获得。
这些流可以提高性能 - 以多线程开销为代价。
在本快速教程中,我们将介绍***Stream API的一个最大限制,**并了解如何使并行流与自定义 ThreadPool实例一起工作,或者 - 用一个库来处理这个问题 https://github.com/pivovarit/parallel-collectors。
2.并行流
让我们从一个简单的例子开始 - 在任何Collection类型上调用parallelStream方法- 这将返回一个可能并行的Stream:
@Test
public void givenList_whenCallingParallelStream(){
List<Long> aList = new ArrayList<>();
Stream<Long> parallelStream = aList.parallelStream();
assertTrue(parallelStream.isParallel());
}
在这样的Stream中发生的默认处理使用ForkJoinPool.commonPool(),它是整个应用程序共享的线程池。
3.自定义线程池
**我们实际上可以在处理流时传递自定义ThreadPool。
以下示例允许并行Stream使用自定义线程池来计算1到1,000,000之间的值之和,示例:
@Test
public void giveRangeOfLongs_Total() throws InterruptedException, ExecutionException {
long firstNum = 1;
long lastNum = 1000000;
List<Long> aList = LongStream.rangeClosed(firstNum, lastNum).boxed().collect(Collectors.toList());
ForkJoinPool customThreadPool = new ForkJoinPool(4);
long actualTotal = customThreadPool.submit(() -> aList.parallelStream().reduce(0L, Long::sum)).get();
assertEquals((lastNum + firstNum) * lastNum / 2, actualTotal);
}
我们使用ForkJoinPool构造函数,并行级别为4.需要进行一些实验来确定不同环境的最佳值,但一个好的经验法则是根据CPU的核心数量选择数字。
接下来,我们处理并行Stream的内容,在reduce调用中将它们相加。
这个简单的例子可能无法证明使用自定义线程池的全部用处,但是在我们不希望将公共线程池与长时间运行的任务(例如,处理来自网络源的数据)联系起来的情况下,这些好处变得明显。,或者应用程序中的其他组件正在使用公共线程池。
4.结论
我们简要介绍了如何使用自定义线程池运行并行Stream。在适当的环境中并且在正确使用并行度级别的情况下,可以在某些情况下获得性能提升。
关注公众号:「Java知己」,每天更新Java知识哦,期待你的到来!
- 发送「1024」,免费领取 30 本经典编程书籍。
- 发送「Group」,与 10 万程序员一起进步。
- 发送「JavaEE实战」,领取《JavaEE实战》系列视频教程。
- 发送「玩转算法」,领取《玩转算法》系列视频教程。