I'm using an iterator to remove a projectile from a list if it goes out of the border of my JPanel. Before using the iterator it would not work, but with the iterator it works so long as I put the method into a try-catch for a ConcurrentModificationException
. The code now works, and successfully removes the projectile from the list, but about 30% of the time, the catch hits and causes a stutter in my program. I don't know why it only catches sporadically, but in the limited time my professor was able to look at it he thought it may have been a synchronization issue.
Here is the loop running my program:
private void executeGameLoop()
{
long nextFrameStart = System.nanoTime();
while(panel.getRunning())
{
do
{
panel.repaint();
nextFrameStart += FRAME_PERIOD;
} while(nextFrameStart < System.nanoTime());
long remaining = nextFrameStart - System.nanoTime();
panel.update();
if (remaining > 0)
{
try
{
Thread.sleep(remaining / 1000000);
}
catch(Throwable e)
{
System.out.println(e.getMessage());
}
}
}
}
This is called by the loop at panel.update
. It handles updating the projectiles for now:
public void update()
{
randX = (int)((Math.random() * getWidth()) - (int)(Math.random() * getWidth()));
randY = (int)((Math.random() * getHeight()) - (int)(Math.random() * getHeight()));
int sizeX;
int sizeY;
try
{
Iterator<Projectile> it = shots.iterator();
for(Projectile a : shots)
{
if(!a.equals(null))
{
sizeX = a.getDisplayX();
sizeY = a.getDisplayX();
if((!checkCoords((int)a.getX(), (int)a.getY(), sizeX, sizeY)) && a.hasTarget())
{
a = null;
if(it.next().equals(null));
it.remove();
}
else if(a.hasTarget())
{
a.update();
}
}
}
}
catch (ConcurrentModificationException e){ System.out.println(e.getMessage() + " Catch"); }
}
These last two method are my mechanism for creating the projectile:
private void createProjectile(int x, int y)
{
total++;
if(shots.size() < shotCount)
{
Projectile temp = new Projectile(randX, randY);
temp.setTarget((x + temp.getSprite().getDisplayImg().getWidth() / 8),
(y - temp.getSprite().getDisplayImg().getHeight() / 8));
temp.setHasTarget(true);
shots.add(temp);
msg("Target: (" + x + ", " + y + ")");
}
}
@Override
public void mouseClicked(MouseEvent e)
{
createProjectile(e.getX(), e.getY());
}
Any insight about why this is happening or how to rectify it would be greatly appreciated.
You have an open Iterator
in your for
loop (plus the extra Iterator it
), and you're adding values in createProjectile
. You need to either make both blocks synchronized
on shots
, or (my recommendation) make a copy of shots
to do your drawing from:
List<Projectile> shotsToPaint;
synchronized(shots) { shotsToPaint = [`ImmutableList`][1].copyOf(shots); }
and apply appropriate synchronization in createProjectile
. Depending on how performance-sensitive it is, you can either synchronize the whole method on shots
or synchronize on shots
, check the size, create the new Projectile
in an unsynchronized block, and then synchronize to recheck the list size and add.