parallelStream()java 1.8 vs 11

parallelStream()java 1.8 vs 11,java,java-8,java-stream,java-11,Java,Java 8,Java Stream,Java 11,考虑以下代码: 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));

考虑以下代码:

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 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

为什么结果不同?

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

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

执行的一种可能顺序是:

T1 calls setId
T1 prints
T2 calls setId
T2 prints
...
T5 calls setId
T5 prints
但是,由于您不做任何事情来确保以原子方式进行设置和打印,因此也允许使用以下命令以及许多其他命令:

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:


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

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

爪哇8

爪哇11


注意,根据javadoc,这种行为是显式的非确定性的,因此两个输出都是有效的执行顺序

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


我想我的问题还不够清楚。问题是:为什么Java11的结果与1.8的结果不同。如果仔细观察,id字段在java 11中是相同的,但在1.8中是不同的。试着看一看它是1.8对9,但应该让您更了解流在1.8搜索中的工作方式,例如java-8流的不一致行为?
T3 calls setId
T1 calls setId
T2 calls setId
T5 calls setId
T4 calls setId

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