处理实际上已完成,但可能需要相当长的时间,具体取决于计算机上的硬件线程数。API文档 https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#limit-long-about limit 警告并行流可能会很慢。
实际上,并行流首先根据可用的并行级别将计算分成几个部分,对每个部分执行计算,然后将结果连接在一起。你的任务有多少部分?每个公共 FJP 线程一个 (=Runtime.getRuntime().availableProcessors()
) 如果当前线程不在 FJP 中,则加(有时?) 1。您可以控制它添加
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "4");
实际上,对于您的任务,您设置的数字越低,计算速度就越快。
无限任务如何拆分?您的特定任务由 Iterator Spliterator 处理trySplit http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/util/Spliterators.java#Spliterators.IteratorSpliterator.trySplit%28%29方法创建从 1024 开始不断增加大小的块。您可以自己尝试:
Spliterator<Integer> spliterator = Stream.iterate(200_000_000, n -> ++n).spliterator();
Spliterator[] spliterators = new Spliterator[10];
for(int i=0; i<spliterators.length; i++) {
spliterators[i] = spliterator.trySplit();
}
for(int i=0; i<spliterators.length; i++) {
System.out.print((i+1)+": ");
spliterators[i].tryAdvance(System.out::println);
}
因此,第一个块处理范围 200000000-200001023 的数字,第二个块处理范围 200001024-200003071 的数字,依此类推。如果您只有 1 个硬件线程,您的任务将被拆分为两个块,因此将检查 3072。如果你有 8 个硬件线程,你的任务将被分成 9 个块,并且将检查 46080 个数字。只有在处理完所有块之后,并行计算才会停止。将任务分割成如此大的块的启发式方法在您的情况下效果不佳,但如果该区域周围的素数在几千个数字中出现一次,您会看到性能提升。
也许您的特定场景可以在内部进行优化(即,如果第一个线程发现已经达到限制条件,则停止计算)。请随时向 Java 错误跟踪器报告错误。
Update在深入挖掘 Stream API 内部之后,我得出结论,当前的行为是一个错误,提出了一个问题 https://bugs.openjdk.java.net/browse/JDK-8148250并发布了一个patch http://cr.openjdk.java.net/~tvaleev/webrev/8148250/r1/。该补丁很可能会被 JDK9 接受,甚至可能向后移植到 JDK 8u 分支。使用我的补丁,并行版本仍然没有提高性能,但至少它的工作时间与顺序流工作时间相当。