小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
在Java8之后ParallelStream横空出世,一举成为优化利器,当一些处理一些较大的列表时可以轻松地将正常的Stream变成并行计算,节省了大量的处理耗时,而且不用关心内部的实现,几乎可以认为是一键优化。一些体量较小的业务场景确实可以这么用,毕竟我们的服务器性能是如此的强大。但在高负载,复杂且庞大的业务中,使用ParallelStream可能还会导致性能降低。
为什么呢?ParallelStream的并行确实省去了很多额外的代码,但较高级的代码就等于较少的控制粒度,有时候接近于失控,不开玩笑。ParallelStream使用的是通用的fork/join线程池,这个线程池默认会创建和cpu核心数相同的线程数,比如我的电脑是4核的,就会是如下几个线程在干活:
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-2
ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-4
当出现2个或以上的ParallelStream,且会涉及一些IO操作时,问题就来了,这些干活的线程就会等IO结束在处理,此时的并行就是假并行了,跟单线程没多少区别。其实机器的性能足够创建更大的线程池,可以更精确地控制IO等待,但就是因为这种一键优化,在不知不觉中降低了性能。
这个默认线程池是可以被替换的,但代码复杂的程度让人觉得还不如用Executor来的更实在。如下:
ForkJoinPool forkJoinPool = new ForkJoinPool(20);
forkJoinPool.submit(() -> {
list.parallelStream().forEach(x -> {}
//some task
);
});
});
那ParallelStream适合什么场景呢?不涉及IO的流,小规模的业务。聊胜于无吧,哈哈。