原文链接:https://www.baeldung.com/java-8-parallel-streams-custom-threadpool
1. 概述
Java 8引入了流的概念去对数据进行复杂的操作,而且使用并行流(Parallel Steams)支持并发,大大加快了运行效率。
在这篇小短文中,我们会看一下Stream API的一个最大的限制,并且我们会展示如何让parallel stream使用我们自定义的线程池。
2. Parallel Stream
我们先来看一个简单的例子,这个例子中,我们对集合类型使用parallelStream
方法,这将会返回一个并行流。
@Test
public void givenList_whenCallingParallelStream_shouldBeParallelStream(){
List<Long> aList = new ArrayList<>();
Stream<Long> parallelStream = aList.parallelStream();
assertTrue(parallelStream.isParallel());
}
默认情况下,Stream使用的是ForkJoinPool.commonPool()
,这是一个公用的线程池,被整个程序所使用。
3. 自定义线程池
实际上,在使用stream时,我们可以使用自定义的线程池。
下面这个例子,我们让parallel stream使用自定义的线程池去计算闭区间1到1,000,000的和。
@Test
public void giveRangeOfLongs_whenSummedInParallel_shouldBeEqualToExpectedTotal()
throws InterruptedException, ExecutionException {
long firstNum = 1;
long lastNum = 1_000_000;
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的核心数量就可以了。
接下来,我们处理并行流的数据,调用reduce
进行加和。
这个例子可能没有完全阐释使用自定义线程池的好处。但是,在一些处理需要长时间运行的任务(例如,处理来自网络流的数据)或者其他部分也在使用common pool时,我们不想让common pool有更多的计算负载。这时候使用自定义的线程池的好处会非常明显。
4. 结论
我们简要的说明了如何使用自定义的线程池运行parallel Stream。在合适的环境,使用了合适的并行度(parallelism level),性能将会有所提高。
文章中完整的代码可以在Github找到。