简体   繁体   English

并行流() java 1.8 与 11

[英]parallelStream() java 1.8 vs 11

Consider the following code:考虑以下代码:

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()
    }
}

When it is compiled and run with java 11, it returns the following:当它使用 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

But with java 1.8, it returns different result:但是对于 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

Why results are different?为什么结果不一样?

Both results are consistent with the Java Memory Model.这两个结果都与 Java Memory Model 一致。

One possible ordering in which execution occurs is:执行发生的一种可能顺序是:

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

but, because you don't do anything to ensure that the set and print occur atomically, the following is also allowed (as are many other orderings):但是,因为您没有做任何事情来确保 set 和 print 原子发生,所以也允许以下操作(与许多其他排序一样):

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

T1 prints
T1 prints
...
T5 prints

So, the reason they're different is because the specification doesn't require them to be the same;因此,它们不同的原因是规范不要求它们相同。 and some subtle (or perhaps not-so-subtle) implementation (or environmental) difference means they execute differently.一些微妙的(或者可能不那么微妙的)实现(或环境)差异意味着它们的执行方式不同。

But, you say, what is the implementation difference?但是,你说,实现上有什么区别? That's not something you should need to care about (which sounds like bluster to cover for not knowing: I really don't).这不是你应该关心的事情(这听起来像是因为不知道而大肆宣传:我真的不知道)。 You should care about the Java Memory Model , because that gives the guaranteed properties.您应该关心 Java Memory Model ,因为这提供了保证的属性。

For example, if you want the "Java 8" behaviour, you can synchronize the threads on a common monitor, for example obj :例如,如果您想要“Java 8”行为,您可以在一个公共监视器上同步线程,例如obj

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

Of course, the threads still will execute in an arbitrary order;当然,线程仍然会以任意顺序执行; but each thread will print the value that it set.但是每个线程都会打印它设置的值。

There is no difference in Java 8 and Java 11, for each run we are getting different results. Java 8 和 Java 11 没有区别,每次运行我们得到不同的结果。 If we want to print properly we can use synchronize block, but we will lose the benefit of parallelStream in this case.如果我们想正确打印,我们可以使用synchronize块,但在这种情况下我们将失去parallelStream的好处。

JAVA 8 JAVA 8

在此处输入图像描述

在此处输入图像描述

JAVA 11 JAVA 11

在此处输入图像描述

在此处输入图像描述

Note that this behaviour is explicitly non deterministic as per the javadoc, so both outputs are valid execution orders.请注意,根据 javadoc,此行为是明确的非确定性的,因此两个输出都是有效的执行顺序。

The behavior of this operation is explicitly nondeterministic.此操作的行为是明确的非确定性的。 For parallel stream pipelines, this operation does not guarantee to respect the encounter order of the stream, as doing so would sacrifice the benefit of parallelism.对于并行 stream 管道,此操作不保证遵守 stream 的遇到顺序,因为这样做会牺牲并行性的好处。 For any given element, the action may be performed at whatever time and in whatever thread the library chooses.对于任何给定的元素,可以在库选择的任何时间和任何线程中执行操作。 If the action accesses shared state, it is responsible for providing the required synchronization.如果操作访问共享 state,它负责提供所需的同步。

I suppose that if you didn't use the forEachOrdered method and instead you're using forEach on the stream it means that each time you should receive different values no matter which JDK you will use.我想如果您没有使用 forEachOrdered 方法,而是在 stream 上使用 forEach,这意味着无论您使用哪个 JDK,每次都应该收到不同的值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM