我正在使用带有并行流的最新Java 8 lambdas处理数据。 我的代码如下:
ForkJoinPool forkJoinPool = new ForkJoinPool(10);
List<String> files = Arrays.asList(new String[]{"1.txt"});
List<String> result = forkJoinPool.submit(() ->
files.stream().parallel()
.flatMap(x -> stage1(x)) //at this stage we add more elements to the stream
.map(x -> stage2(x))
.map(x -> stage3(x))
.collect(Collectors.toList())
).get();
流以一个元素开头,但在第二个阶段添加了更多元素。 我的假设是这个流应该并行运行,但在这种情况下只使用一个工作线程。
如果我从2个元素开始(即我将第二个元素添加到初始列表中),则生成2个线程来处理流等等......如果我没有明确地将流提交给ForkJoinPool。
问题是:它是记录在案的行为还是可能在实施中发生变化?有没有办法控制这种行为,并允许更多的线程,无论初始列表?
答案 0 :(得分:4)
您可以尝试simple-react中的LazyFutureStream或EagerFutureStream Stream实现。两个Streams都会为每个处理单元创建一个CompletableFuture,每个处理单元都可以在一个单独的线程上执行。哪个可能导致更有效的处理(取决于您的实际用例和资源)。
E.g。
LazyFutureStream.parallelBuilder(10)
.of("1.txt")
.flatMap(x -> stage1(x))
.map(x -> stage2(x))
.map(x -> stage3(x))
.collect(Collectors.toList());
或
EagerFutureStream.parallelBuilder(10)
.of("1.txt")
.flatMap(x -> stage1(x))
.map(x -> stage2(x))
.map(x -> stage3(x))
.collect(Collectors.toList());
答案 1 :(得分:3)
您正在观察的是特定于实现的行为,而不是指定的行为。
当前的JDK 8实现查看最外层的流Spliterator
,并将其用作拆分并行工作负载的基础。由于该示例在原始源流中只有一个元素,因此无法拆分,并且该流运行单线程。这适用于flatMap
返回零,一个或仅少数元素的常见(但绝不仅限于)情况,但在它返回大量元素的情况下,它们全部都是顺序处理。实际上,flatMap
函数返回的流被强制进入顺序模式。见ReferencePipeline.java第270行。
&#34;显而易见&#34;要做的是使这个流平行,或者至少不强制它顺序。这可能会也可能不会改善。最有可能的是,它会改善一些事情,但会使其他事情变得更糟。这里肯定要求更好的政策,但我不确定它会是什么样子。
另请注意,用于强制并行流在您选择的fork-join池中运行的技术,通过向其提交运行管道的任务,也是特定于实现的行为。它在JDK 8中以这种方式工作,但将来可能会改变。