Java中无限流的并行处理

Java中无限流的并行处理,java,parallel-processing,java-8,java-stream,Java,Parallel Processing,Java 8,Java Stream,为什么下面的代码不打印任何输出,而如果我们删除parallel,它会打印0,1 IntStream.iterate(0, i -> ( i + 1 ) % 2) .parallel() .distinct() .limit(10) .forEach(System.out::println); 虽然我知道理想的限制应该放在distinct之前,但我的问题更多地与添加并行处理引起的差异有关。返回“无限顺序流”。因此,使序

为什么下面的代码不打印任何输出,而如果我们删除parallel,它会打印0,1

IntStream.iterate(0, i -> ( i + 1 ) % 2)
         .parallel()
         .distinct()
         .limit(10)
         .forEach(System.out::println);
虽然我知道理想的限制应该放在distinct之前,但我的问题更多地与添加并行处理引起的差异有关。

返回“无限顺序流”。因此,使序列流并行并不太有用

根据以下描述:

对于并行流,放松排序约束有时可以实现更高效的执行。如果元素的顺序不相关,则可以更有效地实现某些聚合操作,例如过滤重复项(distinct())或分组减少项(collector.groupingBy())。类似地,本质上与相遇顺序相关的操作,例如limit(),可能需要缓冲以确保正确的顺序,从而破坏了并行性的好处。在流具有遭遇顺序但用户并不特别关心该遭遇顺序的情况下,使用unordered()显式地对流进行反排序可能会提高某些有状态或终端操作的并行性能。然而,大多数流管道,例如上面的“块权重之和”示例,即使在排序约束下也仍然能够有效地并行

在您的案例中似乎就是这样,使用unordered(),它将打印0,1

    IntStream.iterate(0, i -> (i + 1) % 2)
            .parallel()
            .unordered()
            .distinct()
            .limit(10)
            .forEach(System.out::println);

即使没有并行代码,此代码也存在一个主要问题: 在.distinct()之后,流将只有2个元素-因此限制永远不会生效-它将打印这两个元素,然后继续无限期地浪费CPU时间。不过,这可能正是你想要的

有了平行和限制,我相信由于工作分工的方式,问题会进一步恶化。我还没有完全跟踪并行流代码,但我猜:

并行代码在多个线程之间分配工作,这些线程都会无限期地运行,因为它们永远不会完成配额。系统可能会等待每个线程完成,以便将它们的结果组合在一起,以确保顺序的清晰性,但在您提供的情况下,这种情况永远不会发生

在没有订单要求的情况下,每个辅助线程的结果可以在对照全局差异集进行检查后立即使用

毫无限制地,我怀疑使用不同的代码来处理无限流:不是等待所需的10个流填满,而是将结果报告为已发现。这有点像制作一个迭代器,它报告hasNext()=true,首先生成0,然后生成1,然后next()调用永远挂起而不生成结果-在并行情况下,某些东西正在等待多个报告,以便在输出之前可以正确地组合/排序它们,而在串行情况下,它执行可以挂起的操作


我会尝试找出调用堆栈中有无distinct()或limit()的确切区别,但到目前为止,似乎很难浏览相当复杂的流库调用序列。

我知道代码不正确,正如解决方案中所建议的,如果我们将限制移到distinct()之前,我们将不会有无限循环

并行函数使用fork和join概念来分配工作,它为工作分配所有可用线程,而不是单个线程

我们正确地期望无限循环,因为多线程无限地处理数据,并且没有任何东西阻止它们,因为10的限制在distinct之后永远不会达到

它可能一直在尝试分叉,而从未尝试加入以推动其前进。但我仍然认为这是java中的一个缺陷,它比其他任何东西都重要。

真正的原因是有序并行
。distinct()
是文档中的完整屏障操作:

在并行管道中保持
distinct()
的稳定性相对昂贵(要求操作充当完整的屏障,具有大量缓冲开销),并且通常不需要稳定性

“全屏障操作”是指所有上游操作必须在下游开始之前执行。流API中只有两个完整的屏障操作:
.sorted()
(每次)和
.distinct()
(在有序并行情况下)。由于向
.distinct()
提供了非短路无限流,因此最终会出现无限循环。根据契约
.distinct()
不能以任何顺序向下游发送元素:它应该始终发送第一个重复元素。虽然理论上可以更好地实现并行有序
.distinct()
,但实现起来要复杂得多


至于解决方案,@user140547是正确的:在
.distinct()
之前添加
.unordered()
,这将
distinct()
算法切换为无序算法(它只使用shared
ConcurrentHashMap
存储所有观察到的元素,并将每个新元素发送到下游)。请注意,在
.distinct()
之后添加
.unordered()
不会有任何帮助。

在我的机器上,这会将3/4个内核锁定在100%的CPU使用率下,而不会产生答案!我认为这可能是limit和parallel之间交互的一个bug。@1239-同意这应该被视为一个bug;没有明显的原因说明此代码没有及时打印某些内容。它试图在
distinct
op中缓冲整个流内容。即使没有
限制,也会发生这种情况。虽然很明显,处理后续块的线程必须等待它们的前一个有序流,但第一个线程不需要等待…。。@bayou.io:已知的现象是,没有看到OOME,而只有一个进程挂起在GC活动中。只需监视JVM活动、已用内存和最大堆之间的比率以及CPU使用率和GC活动之间的比率。但是,当GC开始发疯时,mo