Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Arraylist具有大量值时抛出ConcurrentModificationException_Java_Arraylist_Concurrentmodification - Fatal编程技术网

Java Arraylist具有大量值时抛出ConcurrentModificationException

Java Arraylist具有大量值时抛出ConcurrentModificationException,java,arraylist,concurrentmodification,Java,Arraylist,Concurrentmodification,这是我的问题,我有一个ArrayList,它包含所有应该呈现到屏幕上的实体 它通过foreach循环这样做 for (Entity e : entities) { g.fillRect(x, y, w, h); } 当使用较少的值(如50)填充时,它工作得非常好,没有错误。也就是说,列表的大小是50。但是,当它大约为1000时,会抛出ConcurrentModificationException并使应用程序崩溃 我知道异常意味着列表在迭代过程中被修改了,但在循环中,is实际上从未对列表

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

它通过foreach循环这样做

for (Entity e : entities) {
    g.fillRect(x, y, w, h);
}
当使用较少的值(如50)填充时,它工作得非常好,没有错误。也就是说,列表的大小是50。但是,当它大约为1000时,会抛出ConcurrentModificationException并使应用程序崩溃

我知道异常意味着列表在迭代过程中被修改了,但在循环中,is实际上从未对列表做过任何事情。在其他地方访问该列表以更新内容,但是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);
    }
}
公共无效更新(双增量){
用于(僵尸z:僵尸){
z、 更新(增量);
}
对于(幸存者:幸存者){
s、 更新(增量);
}
List toRemove=new ArrayList();
List toAdd=new ArrayList();
对于(幸存者:幸存者){
用于(僵尸z:僵尸){
如果(z.碰撞(s)){
添加(新僵尸(s.position,this,zms));
删除。添加;
}
}
}
对于(幸存者s:toRemove){
幸存者。移除;
}
用于(僵尸z:toAdd){
僵尸。添加(z);
}
}
这个方法听起来像是被某种引擎调用的方法,几乎可以肯定是在另一个线程中

for (Entity e : entities) {
    g.fillRect(x, y, w, h);
}
这是运行在你的摆动线程,这是分开的

当您有几个实体时,此操作可能会以原子方式完成。当你拥有大量的实体时,在绘制实体的过程中,你有更高的机会交换到另一个线程中去工作。p> 修复(我假设僵尸和幸存者是实体):

在你的画中:

synchronized(entities)
{
    for (Entity e : entities) {
        g.fillRect(x, y, w, h);
    }
}
这将确保一次只有一个线程可以在其中一个同步块中,迫使它们彼此分离


编辑:这有可能在发生碰撞后绘制帧。如果你的帧速率足够高,这将是完全不可见的。如果您确实开始注意到它,那么您可能需要多做一点工作,以便在更新开始后,直到完全完成才开始绘制

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

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

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

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

我们如何在不看到更多代码的情况下回答这个问题?除非看到循环的主体,否则无法判断。这是一个多线程应用程序吗?我怀疑较高的数字会导致错误,因为它会给另一个线程更多的时间进入并更改某些内容循环体只是一个Graphics.fillRect调用来绘制矩形。它不是多线程的,所以我不确定是什么原因造成的。因为列表没有在那里修改。它在其他地方被修改了,但是在foreach循环期间这些东西不能运行,对吗?@Darren你能提供更多关于它在其他地方被修改的信息吗?您应该在循环中发布代码。如果您可以发布SSCCE,这也会很有帮助。如果在任何情况下它被编辑的位置是,那么它将在另一个线程中运行。Java有自己的事件线程实际上,我个人会使用LinkedList来完成这项任务。这里从不使用随机访问。@Cruncher我也是。但我们不知道索引访问是否在其他地方使用。但不管出于什么原因,ArrayList似乎受到Java程序员的青睐。(请注意,我的帖子只在一般意义上提到列表)我假设有了它,您也将围绕绘制循环进行同步?如果不是,那么这里根本不需要同步块<代码>实体=新列表也是原子的。正如我所说的,我希望paint循环通过同步getter获得实体引用。这个引用一旦得到,仍然有效(尽管它不一定反映最新的状态),因此paint循环可以在没有同步的情况下运行。只要你的get是原子的并且不做任何额外的工作。我认为这个解决方案根本不需要同步。谢谢你的帮助。事实上,我是自己想出来的,但我用的和你贴的差不多。我没有考虑通过swing线程运行的图形调用,而不是游戏。谢谢你提供的信息,帮助澄清了一些事情。@Darren很高兴我能帮忙:)。很多人甚至不知道多线程应用程序!当人们显式调用
repaint()
方法时,这尤其会让人感到不舒服,因为他们没有意识到这只是设置了一个标志,告诉swing线程它应该绘制。
synchronized(entities)
{
    for (Entity e : entities) {
        g.fillRect(x, y, w, h);
    }
}
// pseudo code
newList = update(entities);    // takes some time
synchronized (this) {
    entities = newList;        // is quasi-immediate
}