更新精灵图片时出现ConcurrentModificationException

时间:2019-05-03 06:23:54

标签: java multithreading concurrency 2d-games concurrentmodification

在运行游戏时,我不断收到ConcurrentModificationException,该异常利用多线程创建新的精灵并移动它们。主要问题似乎发生在“火球”的创建和/或移动中。

通过注释掉createNewFireballs方法,我已经能够成功运行我的程序,而没有出现异常。但是,每当我使用createNewFireballs方法时,每当我调用一个更新Fireball Sprite图像的函数时(通常不会发生这种错误),通常都会出现该错误。并且我想知道是否有人可以帮助我找到我的问题的根源并可能解决问题。

public synchronized void createNewFireball() {
    new Thread(new Runnable() {
        public void run() {
            while (gameNotPaused && hero.isNotDead()) {
                fireball = new Fireball(dragon.getX(), dragon.getY());
                fireballs.add(fireball);
            try
                {
                Thread.sleep(100); //Waits for .1 second
                }
            catch(InterruptedException e)
                {
                e.printStackTrace();
                Thread.currentThread().interrupt();
                }
            }
        }
    }).start();
}

    //The problem commonly occurs in the update method, 
    //specifically the line  "FireballIter.next().updateImage(g);"
public synchronized void update(Graphics g) {
    Iterator<Sprite> FireballIter = fireballs.iterator();

    Iterator<Sprite> arrowIter = arrows.iterator();
    while (arrowIter.hasNext()) {
        arrowIter.next().updateImage(g);
    }

    Iterator<Sprite> iterator = sprites.iterator(); 
    while (iterator.hasNext()) {
        iterator.next().updateImage(g);
    }

    while (FireballIter.hasNext()) {
        FireballIter.next().updateImage(g);
    }

}
//Although sometimes it occurs as a result of updateScene, which is 
//called in another method which moves all the "projectile" sprites
public synchronized void updateScene(int width, int height) {

    Iterator<Sprite> arrowIter = arrows.iterator();
    while(arrowIter.hasNext()) {
        Sprite spriteObject = arrowIter.next(); 
        ((Arrow) spriteObject).updateState();   
        if (spriteObject.overlaps(dragon, 350, 350)) {
            dragon.arrowHit();
            System.out.printf("Dragon was hit at %d, %d%n, while arrow was at %d,%d%n", dragon.getX(), dragon.getY(), spriteObject.getX(), spriteObject.getY());
            arrowIter.remove();
        }
    }

    Iterator<Sprite> fireballIter = fireballs.iterator();
    while(fireballIter.hasNext()) {
        Sprite spriteObject = fireballIter.next(); 
        ((Fireball) spriteObject).updateState();    
    }
}

@Override
public synchronized void run() {
    while (model.getGameNotPaused()) {
        model.updateScene(view.getWidth(), view.getHeight());
        view.repaint();
        try {
            Thread.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
            JOptionPane.showMessageDialog(null, "Press R to resume game.");
        }
    }
}

2 个答案:

答案 0 :(得分:0)

使createNewFireball同步没有任何用处:同步仅适用于该方法的执行,而不适用于线程所执行的可运行程序(这很好,因为否则其他方法将无法执行,直到while循环结束)。

fireballs.add放入synchronized块中,注意确保同步正确的事物:

  • 如果仅使用synchronized (this),则将在Runnable上进行同步。
  • 相反,请使用synchronized (TheContainingClass.this)(其中TheContainingClass是包含这些方法的类的名称)。

答案 1 :(得分:0)

假设您的火球是ArrayList<Fireball>,,您还可以考虑将其切换到CopyOnWriteArrayList,这是ArrayList的线程安全变体