并发修改尽管使用同步但异常

2022-09-02 11:40:35
 public synchronized X getAnotherX(){ 
  if(iterator.hasNext()){
   X b = iterator.next();
   String name = b.getInputFileName();
  ...
   return b;
  }
  else{return null;}
 }

尽管声明标头中有同步语句,我仍然在使用iterator.next()的行处得到一个ConcurrentModificationException Exception;这里出了什么问题?


答案 1

ConcurrentModificationException通常与多个线程无关。大多数情况下,发生这种情况是因为您正在修改它在迭代循环的主体中迭代的集合。例如,这将导致它:

Iterator iterator = collection.iterator();
while (iterator.hasNext()) {
    Item item = (Item) iterator.next();
    if (item.satisfiesCondition()) {
       collection.remove(item);
    }
}

在这种情况下,您必须改用该方法。如果要添加到集合中,则同样会发生这种情况,在这种情况下,没有通用解决方案。但是,如果处理列表,则可以使用子类型,并且这具有方法。iterator.remove()ListIteratoradd()


答案 2

我同意上面关于经常发生的陈述,因为在迭代的同一线程中修改集合。但是,这并不总是原因。ConcurrentModificationException

要记住的是,只有当访问共享资源的每个人都同步时,它才能保证独占访问。synchronized

例如,您可以同步对共享变量的访问:

synchronized (foo) {
  foo.setBar();
}

你可以认为你有独家访问它。但是,没有什么可以阻止另一个线程在没有块的情况下做一些事情:synchronized

foo.setBar();  // No synchronization first.

通过坏运气(或墨菲定律,“任何可能出错的东西,都会出错”),这两个线程可以碰巧同时执行。在对一些广泛使用的集合(例如 、 等)进行结构修改的情况下,这可能导致 .ArrayListHashSetHashMapConcurrentModificationException

很难完全防止这个问题:

  • 您可以记录同步要求,例如,插入“在修改此集合之前必须同步”或“首先获取锁”,但这依赖于用户来发现,阅读,理解和应用指令。blahbloo

    有注释,可以帮助以标准化的方式记录这一点;问题是,您必须有一些方法来检查工具链中注释的正确使用。例如,您可能能够使用像Google的errorprone这样的东西,它可以在某些情况下进行检查,但它并不完美javax.annotation.concurrent.GuardedBy

  • 对于对集合的简单操作,您可以使用 Collections.synchronizedXXX 工厂方法,这些方法包装一个集合,以便每个方法调用首先在基础集合上进行同步,例如 SynchronizedCollection.add 方法

    @Override public boolean add(E e) {
      synchronized (mutex) { return c.add(obj); }
    }
    

    其中 是同步的实例(通常是其本身),并且是包装的集合。mutexSynchronizedCollectionc

    这种方法的两个警告是:

    1. 您必须小心,无法以任何其他方式访问包装的集合,因为这将允许非同步访问,这是原始问题。这通常是通过在构造时立即包装集合来实现的:

      Collections.synchronizedList(new ArrayList<T>());
      
    2. 同步是按方法调用应用的,因此,如果您正在执行一些复合操作,例如

      if (c.size() > 5) { c.add(new Frob()); }
      

      那么在整个操作过程中,您没有独占访问权限,只能单独访问 和 调用。size()add(...)

      为了在复合操作期间获得互斥访问,您需要在外部进行同步,例如.但是,这需要您知道要同步的正确内容,这可能是 也可能不是 。synchronized (c) { ... }c


推荐