简体   繁体   English

为什么使用迭代器获取java.util.ConcurrentModificationException?

[英]Why am I getting java.util.ConcurrentModificationException with an iterator?

I'm trying to write a game, so every frame I call the doDraw() method where I'm using an iterator to loop through all GameObjects and print them all on screen: 我正在尝试编写游戏,因此每一帧都调用doDraw()方法,在此我使用迭代器遍历所有GameObject,并在屏幕上全部打印出来:

Iterator<GameObject> itr = mObjList.iterator();
    while (itr.hasNext()) {
        GameObject obj = itr.next(); // this line gives me the error
        ...
        // print object
    }

The only method that adds item to the list, is this: 将项目添加到列表的唯一方法是:

public void click(int x, int y) {
    // adds new object to the list on a click event
    mObjList.add(new GameObject(x, y));
}

Most of the times it works. 在大多数情况下,它都有效。 But sometimes I get this error: 但有时我会收到此错误:

java.util.ConcurrentModificationException

From the line with "itr.next()". 从带有“ itr.next()”的行开始。 From what I've googled, I figured this is because the click() event sometimes happen before the draw() finishes drawing every object, so it's changing the list while the iterator is using it. 根据我的调查,我发现这是因为click()事件有时在draw()完成绘制每个对象之前发生,因此它在迭代器使用它时更改了列表。 I suppose this is what's wrong? 我想这是怎么回事?

But I'm not experienced with threads. 但是我对线程没有经验。 How could I possibly fix this? 我该如何解决? Maybe I'm doing this whole thing wrong and I should use a completely different method to print all objects on screen? 也许我整件事做错了,应该使用完全不同的方法在屏幕上打印所有对象吗?

If the expected number of reads and traversals greatly outnumbers the number of updates to the list, use a CopyOnWriteArrayList . 如果预期的读取和遍历次数大大超过了列表的更新次数,请使用CopyOnWriteArrayList

Otherwise, synchronize on the list (or a dedicated mutex object) when iterating and mutating: 否则,在迭代和变异时,在列表(或专用互斥对象)上进行synchronize

private List<GameObject> mObjList = /* whatever */;
private final Object mListMutex = new Object();

// snip...

synchronized (mListMutex) {
    for (GameObject obj : mObjList) {
        // do your thang
    }
}

// snip...
public void click(int x, int y) {
    GameObject obj = new GameObject(x, y);
    synchronized (mListMutex) {
        mObjList.add(obj);
    }
}

I'd like add to the answer from @aleph_null. 我想从@aleph_null添加到答案。 @aleph_null is correct that this exception happens when you try to modify a collection while you are iterating across it -- only the remove() method on the iterator is allowed. @aleph_null是正确的,当您在对集合进行迭代时尝试修改集合时,会发生此异常-仅允许迭代器上的remove()方法。 The iterator is trying to protect itself from changes happening to the collection underneath it. 迭代器试图保护自己免受其下集合发生的更改。

I would not, however, recommend synchronization as the right solution. 但是,我不建议将同步作为正确的解决方案。 If you need the behavior of adding stuff to a list while you are processing it then I recommend adding to another list and then calling addAll() once you stop iterating. 如果需要在处理列表时将其添加到列表中的行为,那么我建议添加到另一个列表中,然后在停止迭代后调用addAll() More GC intensive for sure but cleaner. 当然,GC强度更高,但更清洁。

Edit: 编辑:

Sorry, I missed the fact that the click() is an asynchronous event handed by another thread. 抱歉,我错过了click()是另一个线程处理的异步事件的事实。 I assumed that the click() was called inside the loop. 我假设在循环内调用了click() You will have to synchronize around the list when you add in click() and around the addAll() . 添加click()addAll()时,必须在列表周围进行同步。 You could use an AtomicReference to record the click and then act on it after the iterator finished but only if you were guaranteed of only one item being clicked on at a time. 您可以使用AtomicReference来记录单击,然后在迭代器完成后对其进行操作,但前提是必须保证一次只单击一项。

The solution is to not use an iterator, or somehow ping-pong between lists so that the one you're modifying is not the one you're iterating through. 解决方案是不使用迭代器,或者以某种方式在列表之间进行乒乓操作,以使您要修改的列表不是您要迭代的列表。 You could also try to use synchronization, but that requires some fairly sophisticated skills to do well without creating deadlocks. 您也可以尝试使用同步,但这需要一些相当复杂的技能才能很好地完成而不会产生死锁。

noooo, synchronization errors are nasty... especially since they seem to occur randomly. 不,同步错误是令人讨厌的...尤其是因为它们似乎随机发生。 While traversing a collection with an iterator, you cannot modify the collection (unless you use the iterator's remove method, but that's an exception). 使用迭代器遍历集合时,您不能修改集合(除非您使用迭代器的remove方法,但这是一个例外)。 Doing so results in the exception you're looking at... but only sometimes. 这样做会导致您正在查看的异常……但仅在某些情况下。 The error will occur only when mObjList.add gets called right when you're traversing the iterator. 仅当遍历迭代器时正确调用mObjList.add时,才会发生该错误。

Your guess about why you're getting the ConcurrentModificationException is right on. 您对为什么会收到ConcurrentModificationException猜测是正确的。

To fix this, you could synchronize on the list itself: 要解决此问题,您可以在列表本身上进行同步:

synchronized(mObjList) {
    Iterator itr = mObjList.iterator();
    while (itr.hasNext()) {
        // ...
    }
}

This will allow your thread to acquire a lock on the list itself. 这将允许您的线程在列表本身上获得锁。 If you wrap your access to the list from the other thread in a similar fashion, it will prevent your threads from stepping on each other (see Intrinsic Locks ). 如果您以类似的方式包装从另一个线程对列表的访问,则将防止您的线程彼此踩踏(请参阅内部锁 )。

I can't tell from context if this would wreak unnecessary havoc in your other thread, but if the list is small-ish it shouldn't be problem. 我不能从上下文中得知这是否会对您的其他线程造成不必要的破坏,但是如果列表很小,那应该不会有问题。 It will certainly cause less problems than an intermittent exception. 它肯定会比间歇性异常引起更少的问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么我得到java.util.ConcurrentModificationException? - Why am I getting java.util.ConcurrentModificationException? 为什么在这个例子中我没有得到 java.util.ConcurrentModificationException ? - Why am I not getting a java.util.ConcurrentModificationException in this example? 为什么我在高级for循环中收到java.util.ConcurrentModificationException? - why am i getting a java.util.ConcurrentModificationException in the advanced for loop? 在Iterator中获取java.util.ConcurrentModificationException - Getting java.util.ConcurrentModificationException in Iterator 带迭代器的java.util.ConcurrentModificationException - java.util.ConcurrentModificationException with iterator 为什么在Java中使用Stack会出现java.util.ConcurrentModificationException? - Why am I getting java.util.ConcurrentModificationException using Stack in Java? java.util.ConcurrentModificationException但我没有删除 - java.util.ConcurrentModificationException But I am not removing 我有一个java.util.ConcurrentModificationException - I am having a java.util.ConcurrentModificationException 当什么都没有删除时,为什么会收到java.util.ConcurrentModificationException? - Why am I getting a java.util.ConcurrentModificationException when nothing is being removed? 知道为什么我在从 HashMap 中删除键时没有收到 java.util.ConcurrentModificationException 吗? - Any Idea why I am not getting java.util.ConcurrentModificationException while removing key from HashMap?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM