简体   繁体   English

Collection.parallelStream() 是否暗示发生在关系之前?

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

Consider this (completely contrived) Java code:考虑这个(完全人为的)Java 代码:

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"?这段代码保证输出“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).似乎不是,除非parallelStream()建立了先发生关系,通过它我们可以确定第一次访问 lambda 中的a[0]将看到100而不是零(根据我的理解Java 内存模型)。

But Collection.parallelStream() is not documented to establish such a relationship...但是Collection.parallelStream()没有记录建立这样的关系......

The same question can be asked for the completion of the parallelStream() method invocation.对于parallelStream()方法调用的完成,可以询问相同的问题。

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?或者... parallelStream()是否真的提供了这些事前发生的关系,而这只是缺少一些文档的问题?

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.我问是因为从 API 设计的角度来看,这似乎(至少对我而言)是合乎逻辑的事情......类似于Thread.start()等。

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. 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);

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.如您所见,那里没有提到parallelStream ,所以要回答您的问题:不, parallelStream本身不会建立事前发生的关系。

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:至于第一次访问读数为零——如果主线程在处理parallelStream之前设置 100,那么paralleStream启动的每个线程都会看到该值,引用链接:

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顺便说一句,您的 lambda 表达式用法是有状态的,这是不鼓励

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.对较少的数字使用并行流不是好的做法。

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

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