Java 使用迭代器的ConcurrentModificationException
我使用迭代器在集合上循环,如下所示:Java 使用迭代器的ConcurrentModificationException,java,iterator,Java,Iterator,我使用迭代器在集合上循环,如下所示: Iterator<Entity> entityItr = entityList.iterator(); while (entityItr.hasNext()) { Entity curr = entityItr.next(); for (Component c : curr.getComponents()) { if (c instanceof Play
Iterator<Entity> entityItr = entityList.iterator();
while (entityItr.hasNext())
{
Entity curr = entityItr.next();
for (Component c : curr.getComponents())
{
if (c instanceof PlayerControlled)
{
((PlayerControlled) c).pollKeyboard();
}
}
}
为什么在我没有改变任何东西的时候会发生这种情况
非常感谢
编辑-堆栈跟踪:
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$1.run(LwjglApplication.java:114)
看起来有另一个线程正在使用同一个集合,并在该代码迭代该集合时对其进行修改
您可以改用navite java。它们是线程安全的。然而,这是一个很好的习惯——它们是线程安全的,并强制您设计可靠的代码 您必须修改列表:
pollKeyboard
方法中,不使用迭代器上的add
或remove
方法;或 Entity curr = entityItr.next();
如果多个线程同时使用该列表:
请注意,此实现是不同步的。如果多个线程同时访问ArrayList实例,并且至少有一个线程在结构上修改列表,它必须在外部进行同步
解决方案:
如果只有一个线程访问列表,请确保使用或add
方法修改列表
对于多线程情况,如果没有可用的锁定对象,则可以使用
首先将对列表的单个中心引用存储为:
entityList = Collections.synchronizedList(theOriginalArrayList);
然后通过以下方式访问它(与所有读卡器和写卡器一起):
还有其他同步多线程访问的方法,包括将列表复制到数组(在同步块内)并对其进行迭代以进行读取,或者使用
ReadWrite
锁。它们都取决于您的确切需求。堆栈跟踪应该对此进行解释。读一读,然后贴出来。异常名称和javadoc也解释了这一点:您在对集合进行迭代时正在修改集合。您确定pollKeyboard
无法以某种方式修改entityList
吗?@SebastianRedl我认为如果pollKeyboard
正在修改entityList
,那么异常将以某种方式从((PlayerControl)c)抛出.poll键盘()代码>非源于实体curr=entityItr.next()代码>。对吗?@EpicPandaForce抛出此错误是为了防止更难发现的更严重的一致性问题。如果进入循环,但在访问元素i
之前,在移除i
之前的某个元素移动其他元素,该怎么办?您将跳过一个元素。使用迭代器允许您在对集合进行迭代时修改集合,只要您使用迭代器的方法修改它。不,迭代器不会再次重建。在每次迭代中,使用相同的迭代器对象来获取下一个元素。同时使用synchronizedList
和synchronized
块似乎有些过分。@SebastianRedl。如果您只想调用add
,可以直接在返回的列表中执行,但对于迭代器,我将再次引用:“synchronizedList返回一个已同步的。。。列表所有访问必须通过返回的列表完成。。。当用户在返回的列表上进行迭代时,必须手动同步该列表。。。未能遵循此建议可能会导致不确定的行为“。在所有访问代码周围只有一个同步块就足够了。这使得额外的包装器确实有些过分。@MarkoTopolniksynchronizedList()
允许您在不迭代时避免同步块,例如add
在封面下执行自己的同步块。但是您仍然使用同步块进行迭代。因此,作为一般模式,您应该使用synchronizedList()
,在其上同步以进行迭代,并使用直接方法进行其他操作。是的,如果您有不同的同步对象并将其用于所有代码,则可以,但必须在每个操作上同步。当然。但更广泛的一点是,synchronizedList
无法强制执行其公共API的线程安全性。事实上,您只获得了一个ev更复杂的契约:同步这个,不要同步那个,并且非常小心不要无意中将列表传递给第三方方法,而第三方方法可能会对列表进行迭代。
synchronized (entityList) {
// Readers might do:
itr = entityList.iterator();
while (i.hasNext())
... do stuff ...
}