当Arraylist具有大量值时,会抛出ConcurrentModificationException

时间:2013-11-25 21:50:11

标签: java arraylist concurrentmodification

所以这是我的问题,我有一个ArrayList,它包含应该呈现给屏幕的所有实体。

它使用foreach循环这样做。

for (Entity e : entities) {
    g.fillRect(x, y, w, h);
}

当使用较少数量的值(例如50)填充时,这完全正常,没有错误。也就是说,列表的大小为50.但是当它大小为1,000时,会抛出ConcurrentModificationException并导致应用程序崩溃。

我知道异常意味着列表在迭代时已被修改,但在该循环中实际上从未对列表做任何事情。在其他地方访问该列表以更新内容,但是在修改列表的其他事情发生之前,foreach循环是否应该完成?

在更新实体的更新方法中修改了列表。

僵尸在某种意义上是敌人,幸存者是人工智能。当僵尸与AI发生碰撞时,它会移除幸存者并将其替换为幸存者。这是列表修改的唯一位置。

当它处理少量实体时,这一切都能很好地工作,但是数量较大的实体会崩溃。

public void update(double delta) {
    for (Zombie z : zombies) {
        z.update(delta);
    }
    for (Survivor s : survivors) {
        s.update(delta);
    }

    List<Survivor> toRemove = new ArrayList<Survivor>();
    List<Zombie> toAdd = new ArrayList<Zombie>();

    for (Survivor s : survivors) {
        for (Zombie z : zombies) {
            if (z.collides(s)) {
                toAdd.add(new Zombie(s.position, this, zms));
                toRemove.add(s);
            }
        }
    }

    for (Survivor s : toRemove) {
        survivors.remove(s);
    }

    for (Zombie z : toAdd) {
        zombies.add(z);
    }
}

2 个答案:

答案 0 :(得分:2)

public void update(double delta)

这种方法听起来像是某种引擎调用的方法,几乎​​可以肯定是在另一个线程中。

for (Entity e : entities) {
    g.fillRect(x, y, w, h);
}

这是在你的摇摆线程中运行的,它是独立的。

如果您拥有少量实体,则此操作可能会以原子方式完成。当你有更多的实体时,你有更高的机会交换到另一个线程,以便在绘制实体的过程中进行工作。

修复(我假设僵尸和幸存者是实体):

synchronized(entities)
{
    for (Survivor s : toRemove) {
        survivors.remove(s);
    }

    for (Zombie z : toAdd) {
        zombies.add(z);
    }
}

在你的油漆中:

synchronized(entities)
{
    for (Entity e : entities) {
        g.fillRect(x, y, w, h);
    }
}

这将确保一次只有一个线程可以在其中一个同步块中,从而强制它们彼此分开发生

编辑:这有可能在发生碰撞后画一帧。如果您的帧速率足够高,这将是完全不明显的。如果你确实开始注意到它,那么你可能需要做更多的工作,这样一旦更新开始,涂料将不会开始直到完成。

答案 1 :(得分:2)

为避免同步,您应该考虑使用只读列表。

也就是说,在update()中,不要从列表中删除,而是将幸存者复制到新列表,然后将僵尸添加到该新列表中。最后,您可以将旧列表的引用替换为新列表中的一个。 这只需要在保存此引用的对象上进行同步。但是因为替换引用非常便宜,同步不会导致另一个trhread等待太久。

// pseudo code
newList = update(entities);    // takes some time
synchronized (this) {
    entities = newList;        // is quasi-immediate
}

不要忘记让实体的getter同步,并且只能通过getter访问实体。