问题
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.
回答1:
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.
回答2:
The for loop is one of the problems. When you create a loop like this:
for (Projectile a : shots) {...}
The compiler implicitly converts it to:
for (Iterator<Projectile> i = shots.iterator; i.hasNext();) {
Projectile a = i.next();
...
}
So in total you have TWO iterators. The first one is created when you explicitly call shots.iterator()
, and the second is created implicitly by the compiler in the for loop.
One cause of ConcurrentModificationException
is when someone else modifies a list at the same time you are iterating over it. Your professor suspected a synchronization issue because usually the "someone else" is in a different thread, but in this case the "Someone else" is the other iterator.
EDIT: As chrylis has pointed out, there is interference from a different thread as well.
来源:https://stackoverflow.com/questions/18752320/trycatch-concurrentmodificationexception-catching-30-of-the-time