使用 Iterator 的 ConcurrentModificationException

ConcurrentModificationException using Iterator

我正在使用迭代器循环遍历集合,如下所示:

Iterator<Entity> entityItr = entityList.iterator(); 

    while (entityItr.hasNext())
    {
        Entity curr = entityItr.next();

        for (Component c : curr.getComponents())
        {
            if (c instanceof PlayerControlled)
            {
                ((PlayerControlled) c).pollKeyboard();  
            }
        }
    }

但是在下一行我得到一个 ConcurrentModificationException

 Entity curr = entityItr.next();

为什么我没有更改任何东西时会发生这种情况?

非常感谢

编辑 - 堆栈跟踪:

java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at cw.systems.Input.checkInputs(Input.java:31)
at cw.systems.Input.begin(Input.java:21)
at cw.misc.Game.render(Game.java:73)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:207)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.run(LwjglApplication.java:114)

看起来有另一个线程使用同一个集合并在这段代码迭代集合时修改它。

ConcurrentModificationException

您可以使用 navite java concurrent collestions instead. They are thread safe. However it's a good habbit to create immutable collections - 它们是线程安全的,并强制您设计可靠的代码。

您必须正在修改列表:

  1. pollKeyboard 方法中的迭代器内部,而不在迭代器上使用 addremove 方法;或
  2. 在另一个线程中

因此您的异常是预期的行为。来自 the docs,如果你有一个单线程迭代列表:

if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException

如果多个线程同时使用该列表:

Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally

解决方案:

如果只有一个线程访问列表,请确保使用 entityItr.removeadd 方法修改列表。

对于多线程情况,如果没有可用的锁定对象,您可以使用 Collections.synchronizedList

首先将您的列表的单个中央引用存储为:

entityList = Collections.synchronizedList(theOriginalArrayList);

然后访问它(与所有读者和作者):

synchronized (entityList) {
  // Readers might do:
  itr = entityList.iterator();
  while (i.hasNext())
    ... do stuff ...
}

还有其他方法可以同步多线程访问,包括将列表复制到数组(在同步块内)并迭代它以进行读取,或使用 ReadWrite 锁。它们都取决于您的具体要求。