[英]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.