我使用parallelStream来获取数组中最长的字符串,代码如下,每次运行时,我都会得到不同的结果。即使在parallelStream 中使用,AtomicReference
也应该是线程安全的?但为什么会发生这种情况?
public static void main(String[] args) {
AtomicReference<String> longest = new AtomicReference<>();
LongAccumulator accumulator = new LongAccumulator(Math::max, 0);
List<String> words = Arrays.asList("him", "he", "thanks", "strings", "congratulations", "platform");
words.parallelStream().forEach(
next -> longest.updateAndGet(
current -> {
String result = next.length() > accumulator.intValue() ? next : current;
accumulator.accumulate(next.length());
return result;
}
)
);
System.out.println(longest.get());
}
有一次,我打印了“congratulations”,有时我打印了“platform”。
最佳答案
您正在调用 LongAccumulator.intValue()
记录为:
并按照指向 get()
方法的链接,我们将学习:
因此,虽然 AtomicReference.updateAndGet
操作是线程安全的,但您对 LongAccumulator.intValue()
和 LongAccumulator.accumulate
的并发调用不是。 LongAccumulator
用于执行并发 accumulate
操作,然后在所有累加操作完成后获取结果。请注意,即使 get()
返回正确的快照,intValue()
的调用和随后的 accumulate()
是两个不同的非原子操作这一事实使该操作仍然容易发生数据竞争。
在大多数情况下,如果您发现自己试图在 forEach
中操作数据结构,那么您使用了错误的工具,使代码变得不必要地复杂且容易出错。
作为克莱恩 hinted in a comment ,words.parallelStream().max(Comparator.comparingInt(String::length))
将简洁而正确地完成工作。