定制Java 8的Parallel Streams使用的线程池

原文链接: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找到。

你可能感兴趣的:(Java)