不确定如何清除 concurrentModificationException。

huangapple go评论143阅读模式
英文:

Not sure how to clear concurrentModificationException

问题

Here is the translated content:

尝试理解为什么我不能创建一个新对象。我有一个生成对象,它缓慢出现,一旦它完成第5帧,它应该生成一个新对象然后消失。它抛出了java.util.ConcurrentModificationException,我查了一下,但不理解它。我以为可能是调用了太多的chicken,所以我尝试创建一个函数从不同的类中调用它。当从其他类调用时,多了一行。它显示了字符串,然后卡在相同的异常上。

@Override
public void die() {
    //this is set up to find the last animation frame
    float x = location.getX();
    float y = location.getY();
    int orient = orientation;
    if(animSpawnRight.getIndex() == 4){
        //spawn chicken
        //then die
        handler.getWorld().getChickenSpawner().setHoleFalse(location);

        //executes to here and doesn't read string

        System.out.print("ARG CHICKENS not working!" + x + " , " + y + " , " + orientation);
        handler.getWorld().getEntityManager().addEntity(new Chicken(handler, x, y, orientation));
        super.setActive(false);
    }
}

错误日志将我带到了我的Entity Manager:

public void tick(){
    Iterator<Entity> it = entities.iterator();
    while(it.hasNext()){
        Entity e = it.next();
        e.tick();
        if(!e.isActive())
             //it.remove() below is the problem
            it.remove();
    }
    entities.sort(renderSorter);
}

目标:ChickenSpawner,基本上监视一个计时器,生成一个chickenSpawning对象。ChickenSpawning对象播放一个短动画,然后调用一个新的chicken。(将spawningChicken和Chicken对象分开是因为确保不能早期射击它更容易。)

(我甚至尝试注释掉:super.setActive(false); 但那也不起作用)。是不是调用了太多的chicken?如果是,我该如何让它只调用一个?我在多线程方面有一些困难。

编辑
e.tick() 调用entity manager持有的所有“entities”的类的tick。

我忘了为什么把die()放在chickenSpawning中 - 可能是为了在完成与所有连接之前看到chickenSpawning动画的超级懒惰方式。

@Override
public void tick() {
    animSpawnUp.tick();
    animSpawnDown.tick();
    animSpawnLeft.tick();
    animSpawnRight.tick(); 
    die();
}

所以它是同时删除和存在吗?

英文:

Trying to understand why I can't create a new object. I have one spawning object that comes up slowly and once it is done with frame 5 it is supposed to spawn a new object and die. It is throwing a java.util.ConcurrentModificationException and I looked it up but don't understand it. I thought maybe it was calling too many chickens so I did try to create a function to call it from a different class. When called from the other class it made it one line more. It said the string but then got hung on the same exception.

@Override
public void die() {
//this is set up to find the last animation frame
float x = location.getX();
float y = location.getY();
int orient = orientation;
    if(animSpawnRight.getIndex() == 4){
        //spawn chicken
        //then die
        handler.getWorld().getChickenSpawner().setHoleFalse(location);

        //executes to here and doesn&#39;t read string

        System.out.print(&quot;ARG CHICKENS not working!&quot; + x + &quot; , &quot; + y + &quot; , &quot; + orientation);
        handler.getWorld().getEntityManager().addEntity(new Chicken(handler, x, y, orientation));
        super.setActive(false);
    }
}

The error log brings me to my Entity Manager:

public void tick(){
    Iterator&lt;Entity&gt; it = entities.iterator();
    while(it.hasNext()){
        Entity e = it.next();
        e.tick();
        if(!e.isActive())
             //it.remove() below is the problem
            it.remove();
    }
    entities.sort(renderSorter);
}

Goal: ChickenSpawner, which basically watches a timer, spawns a chickenspawning object. ChickenSpawning object plays a short animation and then calls a new chicken. (The spawningChicken and Chicken object were separated because it was easier to make sure you can't shoot it early.

(I even tried commenting out: super.setActive(false); but that didn't work either). Is it calling too many chickens? and If so how do I get it to call only one? I have struggled a bit with multithreading.

EDIT
e.tick() calls the classes tick for all "entities" that the entity manager is holding.

And I forgot I put die() in chickenSpawning for some reason - probably a super lazy way to see the chickenspawning animation before finishing how it connected to everything.

@Override
public void tick() {
    animSpawnUp.tick();
    animSpawnDown.tick();
    animSpawnLeft.tick();
    animSpawnRight.tick(); 
    die();
}

So is it deleting and existing at the same time?

答案1

得分: 2

  1. 使用互斥锁来防止其他线程在tick()迭代实体列表时访问或更新它。(并进行排序!) 请注意,当涉及多个线程时,出现CCME可能表明您存在更大的问题。如果多个线程在没有足够同步的情况下访问/更新共享数据结构,可能会出现难以察觉的"内存模型"错误。

  2. 使用CopyOnWriteList来代替列表。它具有确保您不会遇到CCME或"内存模型"错误的并发属性。

@Joni的提出的解决方案处理了CCME,但没有解决潜在的"内存模型"错误。

最后,如果实时性能是一个问题,您可能不应该在每次"tick"时对列表进行排序。事实上,使用ConcurrentSkipListSet而不是列表可能更好。(假设排序标准是恒定的。)

英文:

@Joni is correct in his diagnosis.

I can think of two practical approaches to fixing this:

  1. Use mutexes to prevent any other threads from accessing or updating the entity list while tick() is iterating it. (And sorting it!) Note that a CCME when there are multiple threads involved could be evidence that you have a larger problems. If you have multiple threads accessing / updating a shared data structure without adequate synchronization, you can get insidious "memory model" bugs.

  2. Use a CopyOnWriteList for the list. It has concurrency properties that ensure that you won't get CCMEs or "memory model" bugs.

@Joni's proposed solution deals with the CCMEs but not the potential "memory model"
bugs.

Finally, if real-time performance is a concern you probably should not be sorting the list on every "tick". In fact, it might be better to use a ConcurrentSkipListSet instead of a list. (Assuming that the ordering criteria are constant.)

答案2

得分: 1

尽管名称中含有“ConcurrentModificationException”,但大多数情况下与多个线程并行执行无关。

例如,ConcurrentModificationException 发生在你在迭代列表时向其添加项目。在迭代期间更改数据结构可能会破坏迭代器状态,因此这是不允许的。

看起来这正是你正在做的:tick() 方法通过某个中间步骤调用了 die()。这个 die() 方法调用了 addEntity(),而 addEntity() 则向实体列表中添加了一个项目。

一个可能的修复方法是修改 addEntity(),使其将项目添加到一个单独的列表,并确保不要触碰 entities 集合。

你还需要修改 tick(),以便将单独列表中的新实体添加到主 entities 列表中。

List<Entity> recentlyAddedEntities = new ArrayList<>();

public void addEntity(Entity entity) {
    recentlyAddedEntities.add(entity);
}

public void tick(){
    Iterator<Entity> it = entities.iterator();
    while(it.hasNext()){
         [.. snip ..]
    }
    entities.addAll(recentlyAddedEntities);
    recentlyAddedEntities.clear();
    entities.sort(renderSorter);
}
英文:

Despite the name, ConcurrentModificationException most often has nothing to do with multiple threads executing in parallel.

ConcurrentModificationException happens for example when you add an item to a list while you are iterating on it. Changing a data structure while you iterate on it may break the iterator state, so it's simply not allowed.

It looks like that's exactly what you are doing: The tick() method calls die() through some intermediate step. This die() method calls addEntity(), and addEntity() adds an item to the list of entities.

One possible fix is changing addEntity() so that it adds items to a separate list, and make sure you don't touch the entities collection.

You'll also need to change tick() so that it adds the new entities from the separate list to the primary entities list.

List&lt;Entity&gt; recentlyAddedEntities = new ArrayList&lt;&gt;();

public void addEntity(Entity entity) {
    recentlyAddedEntities.add(entity);
}

public void tick(){
    Iterator&lt;Entity&gt; it = entities.iterator();
    while(it.hasNext()){
         [.. snip ..]
    }
    entities.addAll(recentlyAddedEntities);
    recentlyAddedEntities.clear();
    entities.sort(renderSorter);
}

huangapple
  • 本文由 发表于 2020年8月2日 11:05:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/63211989.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定