2017-06-18 48 views
1

我處理間歇,難以重現ConcurrentModificationException中的遺留代碼塊:瞭解和解決ConcurrentModificationException的

class LegacyCode { 
    private final WeakHashMap<A, B> mItems = new WeakHashMap<A, B>(); 

    public void someWork() { 
     final List<A> copy = new LinkedList<A>(); 

     for (final A item : mItems.keySet()) { 
      copy.add(item); 
     } 

     for (final A item : copy) { 
      item.someMethod(); 
     } 
    } 

    public void addItem(final A item) { 
     mItems.put(item, new B()); 
    } 

    public void removeItem(final A item) { 
     mItems.remove(item); 
    } 
} 

的CME被拋出:

for (final A item : mItems.keySet()) { 
    copy.add(item); 
} 

我不完全確定爲什麼我們以這種方式創建copy。 CME被拋出,因爲在for-each循環正在運行時調用addItem(A)removeItem(A)

問題

  1. 是我爲什麼CME被拋出正確的認識?

  2. 我會盡量避免CME如果我更換了,每個循環:

    final List<A> copy = new LinkedList<A>(mItems.keySet());

  3. 將這種變化等同於for-each循環,我們將取代?據我所知,這兩個片段創建mItems.keySet()copy的淺拷貝。

回答

2

我的理解是爲什麼CME被拋出正確嗎?

絕對。這正是發生的情況。

我會盡量避免CME如果我更換for-each循環:

final List<A> copy = new LinkedList<A>(mItems.keySet()); 

不,你不會的,因爲LinkedList<A>構造將有一個類似的循環。所以你說這個改變將等於我們將要替換的for-each循環是正確的。

就解決這個問題而言,Java標準庫中的WeakHashMap類沒有現成的併發替換。您可以通過使addItemremoveItem同步,並在構建copy的循環周圍添加一個同步塊來解決這個問題。您也可以查看third-party collections解決此問題,而無需在代碼中使用​​。

+0

感謝您解決這個問題。我明白爲什麼要將for-each循環放置在_synchronized_塊中。但是,使'addItem'和'removeItem'同步的原因是什麼? – user3264740

+0

@ user3264740您需要讀取和寫入''synchronized''。否則,儘管'addItem'或'removeItem'可能正在進行,'foreach'循環會自由地獲得鎖。 – dasblinkenlight

+0

令人驚歎的,謝謝! – user3264740