【发布时间】:2018-03-24 09:10:28
【问题描述】:
一个乍一看并不简单的关于拆分器的问题。
在流中,.parallel() 改变了流处理的行为。但是,我期望从顺序流和并行流创建的拆分器是相同的。例如,通常在顺序流中,.trySplit() 永远不会被调用,而在并行流中,它会被调用,以便将拆分拆分器移交给另一个线程。
stream.spliterator() 与 stream.parallel().spliterator() 之间的差异:
-
它们可能具有不同的特征:
Stream.of(1L, 2L, 3L).limit(2); // ORDERED Stream.of(1L, 2L, 3L).limit(2).parallel(); // SUBSIZED, SIZED, ORDERED
这里讨论的似乎是另一个废话流拆分器特征策略(并行计算似乎更好):Understanding deeply spliterator characteristics in java 8 and java 9
-
他们在使用
.trySplit()进行拆分方面可能有不同的行为:Stream.of(1L, 2L, 3L); // NON NULL Stream.of(1L, 2L, 3L).limit(2); // NULL Stream.of(1L, 2L, 3L).limit(2).parallel(); // NON NULL
为什么最后两个有不同的行为?如果我愿意,为什么我不能拆分顺序流? (例如,丢弃其中一个拆分以进行快速处理可能很有用)。
-
将拆分器转换为流时的重大影响:
spliterator = Stream.of(1L, 2L, 3L).limit(2).spliterator(); stream = StreamSupport.stream(spliterator, true); // No parallel processing!
在这种情况下,拆分器是从禁用拆分功能的顺序流创建的(.trySplit() 返回 null)。稍后,需要转换回流时,该流将无法从并行处理中受益。可惜了。
大问题:作为一种解决方法,在调用.spliterator() 之前总是将流转换为并行的主要影响是什么?
// Supports activation of parallel processing later
public static <T> Stream<T> myOperation(Stream<T> stream) {
boolean isParallel = stream.isParallel();
Spliterator<T> spliterator = stream.parallel().spliterator();
return StreamSupport.stream(new Spliterator<T>() {
// My implementation of the interface here (omitted for clarity)
}, isParallel).onClose(stream::close);
}
// Now I have the option to use parallel processing when needed:
myOperation(stream).skip(1).parallel()...
【问题讨论】:
-
期望从顺序流和并行流创建的拆分器是相同的您为什么会这样?您的问题最终不会转变为并行流还是顺序流?
-
单一责任原则。拆分器只是拆分器,他只知道如何拆分迭代器。流应该具有创建非线程的逻辑。 (这是预期)。
-
您可能还记得this answer 的 cmets 中已经讨论了这种特定于实现的行为。在那里,Tagir Valeev 给出了有趣的评论:“另一方面,你不能盲目地使用
parallel(),因为这实际上可能会并行执行一些操作(如排序),意外地消耗更多的 CPU 内核”,它解决了您问题的一部分(始终将流转换为并行的主要影响)...... -
谢谢@Holger。我怎样才能更好地理解影响?这些操作什么时候完成?只在分裂期间? (如果是,可能问题更小,因为顺序流从不调用
.trySplit())。为什么需要进行这些操作?有什么地方可以得到这些信息吗? (顺便说一句:这是我正在寻找的答案)
标签: java java-8 java-stream java-9 spliterator