首页 > 解决方案 > Collection.parallelStream() 是否暗示发生前的关系?

问题描述

考虑这个(完全人为的)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]);

此代码是否保证输出“106”?

似乎不是,除非建立了一个happens-before关系parallelStream(),通过它我们可以确定a[0]lambda中的第一次访问将看到100而不是零(根据我对Java内存模型的理解)。

Collection.parallelStream()没有记录建立这种关系......

parallelStream()对于方法调用的完成,可以询问相同的问题。

那么我是否遗漏了一些东西,或者为了正确起见,上面的代码是否需要看起来像这样:

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]);
}

或者......确实parallelStream()提供了这些发生之前的关系,而这仅仅是缺少一些文档的问题?

我之所以问,是因为从 API 设计的角度来看,这似乎(至少对我而言)是一件合乎逻辑的事情……类似于Thread.start()等。

标签: javajava-streamjava-memory-model

解决方案


您确实应该避免在管道“外部”遇到变量。即使你让它正常工作,性能也可能会受到影响。JDK 内置了很多工具来实现这一点。例如,您的用例可能更安全,例如:

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

推荐阅读