is java AtomicReference thread safe when used within parallelStream?

时间:2016-08-31 12:21:23

标签: java java-8

I used the parallelStream to get the longest string in an array, the code is following, each time it runs, I get different result. AtomicReference supposes to be thread safe even when used within parallelStream? But why does this happen?

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

for one time, I get “congratulations” printed, and some time I get “platform” printed.

2 个答案:

答案 0 :(得分:8)

您正在调用LongAccumulator.intValue(),其记录为:

  

在缩小基元转换后,将current value作为int返回。

并按照get()方法进行操作,我们将学习:

  

返回当前值。返回的值是 NOT 原子快照;在没有并发更新的情况下调用会返回准确的结果,但是在计算值时发生的并发更新可能不会合并。

因此,虽然AtomicReference.updateAndGet操作是线程安全的,但LongAccumulator.intValue()LongAccumulator.accumulate的并发调用不是。 LongAccumulator用于执行并发accumulate操作,然后在所有累积操作完成后获取结果。请注意,即使get()正在返回正确的快照,intValue()和后续accumulate()的调用是两个不同的,因此非原子的操作这一事实使得操作仍然倾向于数据竞赛。

在大多数情况下,如果您发现自己试图在forEach中操纵数据结构,那么您正在使用错误的工具来完成工作,从而使代码变得不必要地复杂且容易出错。 作为Clayn hinted in a comment,a words.parallelStream().max(Comparator.comparingInt(String::l‌​ength))将简明扼要地完成工作。

答案 1 :(得分:3)

实际上我提到了@Holger已经写过的问题,我有点迟了,但我还是写作@ Holger答案的证明。您可以使用 AtomicReference 的累加器;

以下是示例代码:

   public static void main(String[] args) 
   {             
          AtomicReference<String> longest = new AtomicReference<>();
          List<String> words = Arrays.asList("him", "he", "thanks", "strings", "congratulations", "platform");

          words.parallelStream().forEach(next -> {                
              longest.accumulateAndGet(next, (a,b) ->
                      a != null && a.length() > b.length() ? a : b 
                   );                  
             });

          System.out.println(longest.get());
    }