为什么不能并行减少流的流? / stream已经被操作或关闭

时间:2014-08-20 18:56:05

标签: java parallel-processing java-8 java-stream

上下文

我偶然发现了一个相当恼人的问题:我有一个包含大量数据源的程序,它能够传输相同类型的元素,我希望" map"程序中的每个可用元素(元素顺序并不重要)。

因此,我尝试使用Stream<Stream<T>> streamOfStreamOfT;

Stream<T> streamOfT;简化为简单的streamOfT = streamOfStreamOfT.reduce(Stream.empty(), Stream::concat);

由于元素顺序对我来说并不重要,我尝试将reduce操作与.parallel()并行化streamOfT = streamOfStreamOfT.parallel().reduce(Stream.empty(), Stream::concat);但是这会触发java.lang.IllegalStateException: stream has already been operated upon or closed

实施例

通过评论/取消注释.parallel()

,您可以自己体验以下主要内容(java 1.8u20)
public static void main(String[] args) {
    // GIVEN
    List<Stream<Integer>> listOfStreamOfInts = new ArrayList<>();
    for (int j = 0; j < 10; j++) {
        IntStream intStreamOf10Ints = IntStream.iterate(0, i -> i + 1)
                .limit(10);
        Stream<Integer> genericStreamOf10Ints = StreamSupport.stream(
                intStreamOf10Ints.spliterator(), true);
        listOfStreamOfInts.add(genericStreamOf10Ints);
    }
    Stream<Stream<Integer>> streamOfStreamOfInts = listOfStreamOfInts
            .stream();
    // WHEN
    Stream<Integer> streamOfInts = streamOfStreamOfInts
            // ////////////////
            // PROBLEM
            //    |
            //    V
            .parallel()
            .reduce(Stream.empty(), Stream::concat);

    // THEN
    System.out.println(streamOfInts.map(String::valueOf).collect(
            joining(", ")));
}

问题

有人可以解释这个限制吗? /找到一种更好的方法来处理流的并行减少


编辑1

关注@Smutje和@LouisWasserman评论.flatMap(Function.identity())似乎是容忍.parallel()流的更好选择

1 个答案:

答案 0 :(得分:7)

您正在使用的reduce形式采用标识和关联合并功能。但Stream.empty()不是一个值;它有州。流不是数组或集合之类的数据结构;它们是通过可能并行的聚合操作来推送数据的载体,它们具有一些状态(比如流是否被消耗。)想想它是如何工作的;你要构建一棵树,其中同一个“空”流出现在多个叶子中。当你尝试两次使用这个有状态的非同一性(它不会顺序发生,但会并行发生)时,第二次尝试遍历那个空流时,它将被正确地看作已被使用。

所以问题是,你只是错误地使用这个reduce方法。问题不在于并行性;简单来说,并行性暴露了潜在的问题。

其次,即使按照你认为应该的方式“工作”,你也只能建立代表扁平流的树的并行性;当你去加入时,那是一个连续的流管道。糟糕!

第三,即使按照你认为应该的方式“工作”,你也会通过构建连接流来增加很多元素访问开销,而你却不会从并行性中获益你在寻找。

简单的答案就是压缩流:

String joined = streamOfStreams.parallel()
                               .flatMap(s -> s)
                               .collect(joining(", "));