【问题标题】:parallelStream() java 1.8 vs 11并行流() java 1.8 与 11
【发布时间】:2020-08-09 18:28:13
【问题描述】:

考虑以下代码:

public class StreamDemo {
    public static void main(String[] args) {
        StreamObject obj = new StreamObject();
        obj.setName("mystream");

        List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));

        list.parallelStream().forEach(l -> {
            obj.setId(l);
            System.out.println(obj + Thread.currentThread().getName());
        });
    }

    static public class StreamObject {
        private String name;
        private Integer id;

        // getters, setters, toString()
    }
}

当它使用 java 11 编译和运行时,它返回以下内容:

StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-23
StreamObject{name='mystream', id=4}main
StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-9
StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-5
StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-19

但是对于 java 1.8,它返回不同的结果:

StreamObject{name='mystream', id=3}main
StreamObject{name='mystream', id=5}ForkJoinPool.commonPool-worker-2
StreamObject{name='mystream', id=2}ForkJoinPool.commonPool-worker-9
StreamObject{name='mystream', id=1}ForkJoinPool.commonPool-worker-11
StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-4

为什么结果不同?

【问题讨论】:

  • 尝试看看 It is 1.8 vs 9 但应该让您更多地了解流在 1.8 中的工作原理 搜索不一致行为 java-8 流的示例?

标签: java java-8 java-stream java-11


【解决方案1】:

这两个结果都与 Java 内存模型一致。

一种可能的执行顺序是:

T1 calls setId
T1 prints
T2 calls setId
T2 prints
...
T5 calls setId
T5 prints

但是,由于您没有做任何事情来确保 set 和 print 原子发生,因此也允许以下操作(与许多其他排序一样):

T3 calls setId
T1 calls setId
T2 calls setId
T5 calls setId
T4 calls setId

T1 prints
T1 prints
...
T5 prints

所以,它们不同的原因是规范不要求它们相同;以及一些微妙(或者可能不那么微妙)的实现(或环境)差异意味着它们的执行方式不同。

但是,你说,实现的区别是什么?这不是你应该关心的事情(这听起来像是因为不知道而大肆掩饰:我真的不知道)。 您应该关心 Java 内存模型,因为它提供了有保证的属性。

例如,如果你想要“Java 8”的行为,你可以在一个公共监视器上同步线程,例如obj

list.parallelStream().forEach(l -> {
    synchronized (obj) {
        obj.setId(l);
        System.out.println(obj + Thread.currentThread().getName());
    }
});

当然,线程仍然会以任意顺序执行;但是每个线程都会打印它设置的值。

【讨论】:

    【解决方案2】:

    Java 8 和 Java 11 没有区别,每次运行我们都会得到不同的结果。如果我们想正确打印,我们可以使用 synchronize 块,但在这种情况下我们将失去 parallelStream 的好处。

    JAVA 8

    JAVA 11

    【讨论】:

      【解决方案3】:

      请注意,根据 javadoc,此行为是明确的不确定性,因此两个输出都是有效的执行顺序。

      此操作的行为是明确的不确定性。为了 并行流管道,此操作不保证 尊重流的相遇顺序,因为这样做会牺牲 并行性的好处。对于任何给定的元素,动作可能是 在图书馆选择的任何时间和任何线程中执行。 如果动作访问共享状态,它负责提供 所需的同步。

      【讨论】:

        【解决方案4】:

        我想如果您没有使用 forEachOrdered 方法,而是在流上使用 forEach,这意味着无论您使用哪个 JDK,每次都应该收到不同的值。

        【讨论】:

        • 我认为我的问题不够清楚。问题是:为什么 java 11 的结果与 1.8 的结果不同。仔细看,java 11 中的 id 字段是一样的,但在 1.8 中是不同的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-02-10
        • 1970-01-01
        • 1970-01-01
        • 2021-05-20
        • 2014-07-24
        • 2019-08-27
        • 1970-01-01
        相关资源
        最近更新 更多