2016-08-31 89 views
6

我使用parallelStream來獲取數組中最長的字符串,代碼如下,每次運行時,我都會得到不同的結果。即使在parallelStream中使用時,AtomicReference是否仍然是線程安全的?但爲什麼會發生?是java的AtomicReference在parallelStream中使用時線程安全嗎?

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

有一次,我得到「祝賀」打印,有的時候我打印出「平臺」。

+3

是否使用選項'words.parallelStream()最大(Comparator.comparingInt(字符串長度::));'代替。?我試過了,它總是返回相同的單詞。但是,如果它並行執行,我不是100%確定的。 – Clayn

+1

@Clayn:這就是併發問題 - 你永遠無法確定。但我可以斷言你,你的變體是正確的。 – Holger

+1

@Holger感謝您的斷言。我想到的第一件事是:我相信使用Stream API可以做得更加優雅 – Clayn

回答

8

要調用LongAccumulator.intValue()其記錄爲:

返回current value作爲基本收縮轉換後的int。

和之後的臨客爲get()方法,我們將瞭解到:

返回當前值。返回值是不是原子快照;在沒有併發更新的情況下調用會返回一個準確的結果,但在計算該值時發生的併發更新可能不會被合併。

因此,儘管AtomicReference.updateAndGet操作是線程安全的,您的LongAccumulator.intValue()LongAccumulator.accumulate併發調用是沒有的。 A LongAccumulator用於執行併發的accumulate操作,然後在之後取回結果所有累計操作已完成。請注意,即使get()正在返回正確的快照,調用intValue()和後續的accumulate()是兩個不同的,因此非原子操作的事實使操作仍然傾向於數據競爭。

在大多數情況下,如果您發現自己試圖操縱forEach中的數據結構,那麼您使用的工具是錯誤的工具,從而導致代碼不必要的複雜且容易出錯。 由於Clayn hinted in a commentwords.parallelStream().max(Comparator.comparingInt(String::l‌​ength))將做簡潔和正確的工作。

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()); 
    } 
相關問題