我使用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 commentwords.parallelStream().max(Comparator.comparingInt(String::l‌​ength)) 将简洁而正确地完成工作。

10-06 12:37