1

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'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);
    }
}

The error log brings me to my 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);
}

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?

user2899211
  • 63
  • 1
  • 8
  • 1
    (1) Post the actual stack trace, not just "is the problem". (2) What is in `e.tick()`? Is it that method itself? Specifically, is it recursive in such a way that you'll have interlocking lists of some sort? – chrylis -cautiouslyoptimistic- Aug 02 '20 at 03:15

2 Answers2

2

@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.)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
1

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<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);
}
Joni
  • 108,737
  • 14
  • 143
  • 193