请考虑以下代码:
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 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
但是对于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
为什么结果不同?
两个结果均与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内存模型,因为它提供了有保证的属性。尝试看一下它是1.8 vs 9,但应该让您更多地了解流在1.8中的工作方式
我想如果您不使用forEachOrdered方法,而是在流上使用forEach,则意味着无论您使用哪种JDK,每次您都应该接收不同的值。