Does Collection.parallelStream() imply a happens-before relationship?

隐身守侯 提交于 2020-06-27 07:42:04

问题


Consider this (completely contrived) Java code:

final List<Integer> s = Arrays.asList(1, 2, 3);
final int[] a = new int[1];
a[0] = 100;
s.parallelStream().forEach(i -> {
    synchronized (a) {
        a[0] += i;
    }
});
System.out.println(a[0]);

Is this code guaranteed to output "106"?

It seems like it is not unless there is a happens-before relationship established by parallelStream(), by which we can know for sure that the first accesses to a[0] in the lambda will see 100 and not zero (according to my understanding of the Java memory model).

But Collection.parallelStream() is not documented to establish such a relationship...

The same question can be asked for the completion of the parallelStream() method invocation.

So am I missing something, or is it true that for correctness would the above code be required to look something like this instead:

final List<Integer> s = Arrays.asList(1, 2, 3);
final int[] a = new int[1];
synchronized (a) {
    a[0] = 100;
}
s.parallelStream().forEach(i -> {
    synchronized (a) {
        a[0] += i;
    }
});
synchronized (a) {
    System.out.println(a[0]);
}

Or... does parallelStream() actually provide these happens-before relationships, and this simply a matter of some missing documentation?

I'm asking because from an API design perspective, it seems (to me at least) like this would be a logical thing to do... analogous to Thread.start(), etc.


回答1:


You really should avoid hitting variables 'outside' the pipeline. Even if you get it to work correctly performance will likely suffer. There are a lot of tools to achieve this built into the JDK. For example your use case is probably safer with something like:

Integer reduce = IntStream.of(1, 2, 3)
            .parallel()
            .reduce(100, (accumulator, element) -> accumulator + element);



回答2:


Here is a list of actions that establish a happens-before relationship. As you can see parallelStream is not mentioned there, so to answer your question: no, parallelStream by itself doesn't establish a happens-before relationship.

As to the first access reading zero - If the main thread sets 100 before the parallelStream is being processed, then each thread the paralleStream starts will see that value, quoting from the link:

A call to start on a thread happens-before any action in the started thread.

BTW, your lambda expression usage is stateful, which is discouraged




回答3:


I personally use the below code to guaranteed,

        final List<Integer> s = Arrays.asList(1, 2, 3);
        AtomicInteger atomicInteger = new AtomicInteger(100);
        s.parallelStream()
                .forEach(atomicInteger::addAndGet);
        System.out.println(atomicInteger.get());

Using parallel stream for fewer numbers is not good practice.



来源:https://stackoverflow.com/questions/53906027/does-collection-parallelstream-imply-a-happens-before-relationship

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!