LDG LDG - 22 days ago 5
Android Question

java.util.ConcurrentModificationException in Android Game Loop

I am using a canvas, moving objects on the screen, when an object hit the left side of the canvas (x=0), another object of the same type gets instantiated, and start moving on the screen.

Everything works fine, a few objects gets created and start moving around the screen.

At a certain point, I receive a concurrent modification exception in my run method where the game loop is, where gameObjs is an ArrayList:

@Override
public void run() {

while(isRunning){
if(!myHolder.getSurface().isValid())
continue;
Canvas canvas = myHolder.lockCanvas();
canvas.drawRect(0,0,canvas.getWidth(), canvas.getHeight(), pWhite);


for(MyGameObject gameObj : gameObjs){
gameObj.move(canvas);
}

myHolder.unlockCanvasAndPost(canvas);
}

}


I've tried to use an Iterator, but still getting the same error.

I really appreciate your help. Thank you in advance!

Answer

Collections.synchronizedList(...) won't work if something like this is happening... (throws a ConcurrentModificationException...)

public class ConcurrentTest {

    public static void main(String[] args) {
        List<String> things = new ArrayList<>();

        Runnable modifyThread = () -> {
            while(true) {
                for(int k = 0; k < 1000; k++) {
                    things.add(String.valueOf(k));
                }

                while(!things.isEmpty()) {
                    things.remove(0);
                }
            }
        };

        Runnable readThread = () -> {
            while(true) {
                for(String thing : Collections.synchronizedList(things)) {
                    System.out.println(thing);
                }
            }
        };

        new Thread(modifyThread).start();
        new Thread(readThread).start();
    }
}

Try and find other places in your code where this list is being modified. There must be another thread manipulating the collection while you are iterating through it.

You could take a copy of the list before iterating over it.

For example, in the code above, try...

for(String thing : new ArrayList<>(things)) {

...instead of...

for(String thing : Collections.synchronizedList(things)) {

...and you will no longer get an exception (but this still won't be "correct", if you run it, you will see loads of nulls being printed out...)

Even better, keep the read loop as...

for(String thing : things) {

...but change the type of list...

List<String> things = new CopyOnWriteArrayList<>();