TryCatch ConcurrentModificationException catching `30% of the time

孤街醉人 提交于 2019-12-11 03:33:48

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!