2017-06-18 83 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