Java 8并行流自定义线程池

目录

前言

默认并发数不靠谱

通用池耗尽

自定义线程池


前言

         Java 8引入了并行流(Parallel Streams),流会自动通过一个通用的Fork/Join池并行地执行,这个通用Fork/Join池可以使用ForkJoinPool.commonPool()来访问。

默认并发数不靠谱

         通常的通用池的期望并发数的默认值是Runtime.getRuntime().availableProcessors() -1,也就是逻辑CPU核心数减去1。根据《Java并发编程实战》作者 Brian Goetz所指出的,“虚拟机其实不清楚什么是处理器,它只是去请求操作系统返回一个值。同样的,操作系统也不知道怎么回事,它是去问的硬件设备。硬件会告诉它一个值,通常来说是硬件线程数。操作系统相信硬件说的,而虚拟机又相信操作系统说的。”根据Runtime.getRuntime().availableProcessors() 返回的值并不一定能创建出合适数量的工作线程。如果在同一机器上运行着多个 JVM,则应限制这个线程数,以防这些 JVM 彼此争用 CPU。设置通用 Fork/Join池的大小和设置其他任何线程池同样重要。启动的时候,可以通过系统属性 java.util.concurrent.ForkJoinPool.common.parallelism来设置通用池的并发数。如下,设置线程数4,在笔者6核心12线程的CPU上面打印为4。

jvm参数:-Djava.util.concurrent.ForkJoinPool.common.parallelism=4
 System.out.println(ForkJoinPool.commonPool().getParallelism());
打印:4

通用池耗尽

       一旦这个通用池到达了期望并发数的上限,被其他资源占用尽,它就不会再创建新的线程了。尽管可能有很多任务正在等待。这样的话,可能会导致严重的性能瓶颈!比如两个并行流使用相同的线程池,会因为相互等待而被阻塞,这种情形下并行流stream().parallel()还不如普通顺序执行来得快。

自定义线程池

      既然通用Fork/Join池有这么多问题,我们可以使用自定义线程池来避免。通过创建自己的线程池,避免共享线程池,如果有需要,甚至可以分配比处理机数量更多的线程。如下代码展示了并行流如何结合自定义线程池。

  

//创建一个线程数为2的线程池
ForkJoinPool forkJoinPool = new ForkJoinPool(2);
//在自定义线程池里面实现并行计算质数,省略了PrimesPrint::isPrime源码
forkJoinPool.submit(() ->
    //parallel task here, for example
    IntStream.range(1, 1_000_000).parallel().filter(PrimesPrint::isPrime).collect(toList())
).get();

 

你可能感兴趣的:(java,线程池,并行流)