Skip to main content
gamedev.net
🔒 Locked

[java] ConcurrentModificationException

Started by Kevinator 10 replies 2.3k views
Original Post
Kevinator
Kevinator
Hey guys. I'm working on an old-fashioned up-and-down-scrolling space shooter in Java. Today I implemented a scrolling starfield to give the appearance that the player's ship is moving. What I'm doing is randomly generating one star every few frames. Then I add that star to a linked list of stars so that I can keep track of their positions in order to display them. This works and looks very well. However, for obvious reasons, if left running, the program bogs down because the linkedlist becomes enormous and the application must iterate through this gigantic list every frame. So of course I decided to remove stars from this linkedlist once they are no longer in view. But it seems like whenever I try to remove them from the list, I get a runtime error: the concurrentModificationException. I know vaguely what that means: I think it means that I'm not supposed to make changes to the list while I'm still iterating over it. But how am I supposed to know which stars to delete if I can't iterate over the list?!
rip-off
rip-off
Im not 100 % sure, But i think it means you have iterators elesewhere belonging to this list. I believe that if you only have 1 iterator to the list, then the exception will not be thrown.

Yes, looking at the documentation that seems to be the case, excpet that if you editing the list directly ( not using the iterator ) can also cause the exception.

What is your code like?
Kevinator
Kevinator
import java.awt.*;import javax.swing.*;import java.awt.image.*;import java.awt.event.*;import java.util.*;public class Ascendancy implements KeyListener{        private boolean gameRunning = true;        private int x = 100;        private int y = 100;        private int vx = 0;        private int vy = 0;        private int speed = 4;        private int size = 50;        private boolean up = false;        private boolean down = false;        private boolean left = false;        private boolean right = false;        private boolean deccel = false;        //private boolean accel = false;        private int dc = 1;        private int rate = 20;        private LinkedList<Star> stars = new LinkedList<Star>();        public Ascendancy()        {                JFrame f = new JFrame("Ascendancy");                f.setSize(800, 600);                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                f.addKeyListener(this);                f.setVisible(true);                f.createBufferStrategy(2);                //start game                game(f.getBufferStrategy());        }        public void game(BufferStrategy s)        {                //main game loop                while (gameRunning)                {                        Graphics g = s.getDrawGraphics();                        updateWorld();                        renderWorld(g);                        s.show();                        sleep();                        if (deccel)                                dc++;                }        }public void renderWorld(Graphics g)        {                g.setColor(Color.BLACK);                g.fillRect(0, 0, 800, 600);                drawSF(g);                g.setColor(Color.GRAY);                drawFighter(g, x, y);        }        public void updateWorld()        {                updateSF();                double d = Math.random();                if (d > 0.5)                        createStar((int)Math.round(Math.random()*800));                x += vx;                y += vy;                if (deccel)                {                        if (dc % rate == 0 && vx != 0 && vy != 0)                        {                                if (vx > 0)                                        vx--;                                else                                        vx++;                                if (vy > 0)                                        vy--;                                else                                        vy++;                                if (vx == 0 && vy == 0)                                        dc = 0;                        }                }        }        public void sleep()        {                try                {                        Thread.sleep(10);                }                catch (Exception e)                {                }        }        public static void main(String[] args)        {                new Ascendancy();        }public void keyPressed(KeyEvent e)        {                switch (e.getKeyCode())                {                        case KeyEvent.VK_ESCAPE:                                System.exit(0);                                break;                        case KeyEvent.VK_UP:                                up = true;                                dc = 1;                                deccel = false;                                vy = -1 * speed;                                break;                        case KeyEvent.VK_DOWN:                                down = true;                                dc = 1;                                deccel = false;                                vy = speed;                                break;                        case KeyEvent.VK_LEFT:                                left = true;                                dc = 1;                                deccel = false;                                vx = -1 * speed;                                break;                        case KeyEvent.VK_RIGHT:                                right = true;                                dc = 1;                                deccel = false;                                vx = speed;                                break;                }        }public void keyReleased(KeyEvent e)        {                switch (e.getKeyCode())                {                        case KeyEvent.VK_UP:                                up = false;                                deccel = true;                                break;                        case KeyEvent.VK_DOWN:                                down = false;                                deccel = true;                                break;                        case KeyEvent.VK_LEFT:                                left = false;                                deccel = true;                                break;                        case KeyEvent.VK_RIGHT:                                right = false;                                deccel = true;                                break;                }        }        public void keyTyped(KeyEvent e)        {        }        public void updateSF()        {                for (Star s : stars)                {                        s.y = s.y+5;                        if (s.y > 600)                                stars.remove(s);                }        }        public void purgeSF()        {        }        public void createStar(int x)        {                stars.add(new Star(x, 0));        }public void drawSF(Graphics g)        {                g.setColor(Color.WHITE);                for (Star s : stars)                {                        g.drawLine(s.x, s.y, s.x, s.y+10);                }        }        public void drawFighter(Graphics g, int x, int y)        {                g.drawLine(x, y, x+5, y+20);                g.drawLine(x, y, x-5, y+20);                g.drawLine(x+5, y+20, x+5, y+40);                g.drawLine(x-5, y+20, x-5, y+40);                g.drawLine(x+5, y+40, x+25, y+55);                g.drawLine(x-5, y+40, x-25, y+55);                g.drawLine(x-25, y+55, x-5, y+55);                g.drawLine(x+25, y+55, x+5, y+55);                g.drawLine(x+5, y+55, x+5, y+60);                g.drawLine(x-5, y+55, x-5, y+60);                g.drawLine(x+5, y+60, x+10, y+65);                g.drawLine(x-5, y+60, x-10, y+65);                g.drawLine(x-10, y+65, x-5, y+65);                g.drawLine(x+10, y+65, x+5, y+65);                g.drawLine(x+5, y+65, x+4, y+68);                g.drawLine(x-5, y+65, x-4, y+68);                g.drawLine(x-4, y+68, x+4, y+68);                g.setColor(Color.ORANGE);                g.drawLine(x-4, y+68, x, y+75);                g.drawLine(x+4, y+68, x, y+75);        }}


Sorry for the lack of documentation right now. The methods of interest are createStar(), drawSF(), updateSF(), as those are the ones that deal with the linkedlist (called stars).
rip-off
rip-off
Hmm haven't really used Java 1.5, but it seems this contains a hidden iterator, which gets worried wehen you directly edit the list (stars.remove )

for (Star s : stars){    s.y = s.y+5;    if (s.y > 600)          stars.remove(s);}


Try writing it out longhand and see if the error still occurs:

for ( ListIterator<Star> it = stars.getListIterator() ; it.hasNext() ; ){   Star s = it.next();    s.y = s.y+5;    if (s.y > 600)          it.remove();   }


Mind you I haven't used java in a bit, and haven't more than read up on 1.5, so it might not even compile, let alone fix your problem.

Hope it helps either way. Good Luck!
RobAU78
RobAU78
Ah, yes, the good old ConcurrentModificationException (try saying that -- or typing it! -- three times fast [smile]). I've encountered this problem before as well. There is actually a simple solution that still lets you use the new for-each construct in 1.5. All you need to do is implement another list for stars that need to be removed. For each frame, every star that needs to be removed is put in that list. Once the program is done running through the main list of stars, you simply call

stars.removeAll(removedStars);


This approach is easy to use and completely takes away your ConcurrentModificationException worries. It's worked for me with e.g. removing missiles once they go beyond the bounds of the screen (or map).

Hope this helps. [smile]
Kevinator
Kevinator
Thanks a lot fellas. You all were very helpful. I've got it working like a dream now thanks to your advice. Ratings++ for the lot of ya. [wink] Now it's time to refactor that awful code. [grin]
Kevinator
Kevinator
Hmph.

I have another problem. Maybe you guys can help. For some reason, the game gets slower and slower until I'm only getting about 3 or 4 FPS (after about an hour).

I've been monitoring the memory usage and it's remained relatively static. I just don't know what's causing this slowdown. It starts off smooth as a baby's bottom! The only real thing I changed was this:

public void updateSF()        {                for (Star s : stars)                {                        s.updatePos();                        if (s.gone())                                starsAwaitingRemoval.add(s);                }                stars.removeAll(starsAwaitingRemoval);        }
Kevinator
Kevinator
Hahahhaa, nevermind. I just realized that I'm never emptying starsAwaitingRemoval Someone just shoot me, please. [grin]
5MinuteGaming
5MinuteGaming
It'd be nice to see a working prototype Kevinator.

I've always been interested in seeing some of the games that people post questions for on here. I'd like to think that a lot of progress is being made rather than just a crap load of question upon question without anything to show for all of it.

Also, I'm curious how well the scrolling starfield worked out! [grin]
Chaoslab
Chaoslab
I have to ask why I you making this so complicated?

I would of done it like this, could be placed in its own class.

final int numStars = 30;
double starX[] = new double [numStars];
double starX[] = new double [numStars];

All you need to do is when a star passes of the side of you place it on the other side with a random y and like wise for sides the leave the top / bottom of the screen.

link lists for something as simple as this is overkill, kind of like shooting an ant with a cannon and is much more efficient CPU wise.

And you could even add color as well!

Just some ideas.... :-)



ujhkfkfk
Kevinator
Kevinator
Thanks for the idea. That is a much better idea than how I'm doing it. [grin]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.