考虑以下简单代码:
Stream.of(1)
.flatMap(x -> IntStream.range(0, 1024).boxed())
.parallel() // Moving this before flatMap has the same effect because it's just a property of the entire stream
.forEach(x -> {
System.out.println("Thread: " + Thread.currentThread().getName());
});
很长时间以来,我认为Java即使在flatMap
之后也可以对元素进行并行执行。但是上面的代码显示了所有的“ Thread:main”,这证明了我的想法是错误的。
在flatMap
之后使其平行的一种简单方法是收集然后再次流式传输:
Stream.of(1)
.flatMap(x -> IntStream.range(0, 1024).boxed())
.parallel() // Moving this before flatMap has the same effect because it's just a property of the entire stream
.collect(Collectors.toList())
.parallelStream()
.forEach(x -> {
System.out.println("Thread: " + Thread.currentThread().getName());
});
我想知道是否有更好的方法,以及关于flatMap
的设计选择,该选择只能在调用之前并行化流,而不能在调用之后并行化。
==========关于此问题的更多说明========
从某些答案看来,我的问题似乎没有完全传达出来。正如@Andreas所说,如果我从3个元素的Stream开始,则可能有3个线程在运行。
但是我的问题确实是:this post指出,Java Stream使用一个通用的ForkJoinPool,其默认大小等于内核数的一倍。现在假设我有64个内核,那么我希望上面的代码在flatMap
之后会看到许多不同的线程,但是实际上,它只看到一个(在Andreas的情况下为3)。顺便说一句,我确实使用isParallel
来观察流是并行的。
说实话,我并不是出于纯粹的学术兴趣问这个问题。我在一个项目中遇到了这个问题,该项目提出了用于转换数据集的一长串流操作。链从单个文件开始,并通过flatMap
爆炸到许多元素。但是显然,在我的实验中,它不能完全利用我的计算机(具有64个内核),而仅使用一个内核(从对cpu使用情况的观察)。
答案 0 :(得分:0)
我想知道
flatMap
的设计选择是仅在调用之前并行化流,而不在调用之后并行化。
您误会了。 flatMap
之前和之后的所有步骤都是并行运行的,但是它只会在线程之间分割原始流。 flatMap
操作然后由一个这样的线程处理,并且其流不会被拆分。
由于原始流仅包含1个元素,因此无法拆分,因此parallel
不起作用。
尝试更改为Stream.of(1, 2, 3)
,您会看到forEach
在flatMap
的之后,实际上在3个不同的线程中运行。 / p>
答案 1 :(得分:0)
https://www.learnopencv.com/image-alignment-feature-based-using-opencv-c-python/指定:
对于任何给定的元素,可以在库选择的任何时间和线程中执行操作。
特别是,“执行调用线程上的所有操作”似乎是一个很好的广泛安全的实现。
请注意,您尝试并行化流并不会要求任何特定的并行性,但是您更有可能看到此效果:
IntStream.range(0, 1024).boxed()
.parallel()
.map(i -> "Thread: " + Thread.currentThread().getName())
.forEach(System.out::println);
答案 2 :(得分:0)
对于像我这样迫切需要并行化 flatMap 并且需要一些实用解决方案的人,不仅仅是历史和理论。对于那些在并行化它们之前不考虑收集它们之间的所有项目的人。
我想出的最简单的解决方案是手动进行展平,基本上是将其替换为 map + reduce(Stream::concat)
。
我已经在另一个话题中回答了同样的问题,详情请见 https://stackoverflow.com/a/66386078/3606820