【问题标题】:Getting errors related to threads and arrayLists获取与线程和 arrayLists 相关的错误
【发布时间】:2012-12-19 17:29:56
【问题描述】:

您好,我正在编写一个非常简单的游戏。玩家可以使用鼠标移动飞船,每 200 毫秒发射一次新光束。该光束在 while(true) 循环中移动,当它的 y 为 0 或 400(帧的边界)时,我使用 break 来结束循环(和线程)。每根梁都有自己的线程。还有在背景中移动的星星。他们每个人都像梁一样移动,并有自己的线程。如您所见,arrayLists 中经常有添加和删除。一切正常,但有时我会收到这样的错误:

Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
    at java.util.ArrayList$Itr.next(ArrayList.java:791)
    at spacecommander.MainPanel.paintComponent(MainPanel.java:50)

他们在游戏中没有任何问题,但我怎样才能消除他们?也许我应该使用同步之类的?

编辑:这是代码

public class MainPanel extends JPanel {
    private Player player = new Player(100, 100, 3, 3);
    private Point2D targetPoint = new Point2D.Float(130, 350); //Poczatkowa pozycja statku
    private ArrayList<Beam> beams = new ArrayList<Beam>();
    private InputMap imap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
    private ActionMap amap = getActionMap();
    private Random rand = new Random();

    public MainPanel() {
        setPreferredSize(new Dimension(300, 400));

        addMouseMotionListener(new MouseMotionHandler());

        //Rozpoczynanie watkow
        Thread t = new Thread(new PlayerMoveRunnable());
        t.start();
        Thread t2 = new Thread(new PlayerShootRunnable());
        t2.start();
    }

    public void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D)g;
        g2.setColor(Color.BLACK);
        g2.fillRect(0, 0, 300, 400);
        //Rysowanie gracza
        g2.drawImage(player.getImage(), (int)player.getX(), (int)player.getY(), null);
        //Rysowanie pociskow
        for (Beam beam : beams) {
            g2.drawImage(beam.getImage(), (int)beam.getX(), (int)beam.getY(), null);
        }
    }

    public void makeShortcut(String name, String keys, AbstractAction action) {
        imap.put(KeyStroke.getKeyStroke(keys), name);
        amap.put(name, action);
    }

    //Watek dziala caly czas bo gracz i tak caly czas sie rusza
    private class PlayerMoveRunnable implements Runnable {
        public void run() {
            try {
                while (true) {
                    player.moveToPoint(targetPoint);
                    repaint();
                    Thread.sleep(15);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //Takze dziala caly czas. Dodaje nowy pocisk co 200ms
    private class PlayerShootRunnable implements Runnable {
        public void run() {
            try {
                while (true) {
                    //Wybranie pocisku do wystrzelenia w zaleznosci od mode gracza
                    Thread t;
                    switch (player.getBeamMode()) {
                    case 1:
                        t = new Thread(new BeamMoveRunnable(new Beam1(100, 100, 10, 10, 10)));
                        break;
                    }
                    t.start();
                    Thread.sleep(200);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private class BeamMoveRunnable implements Runnable {
        private Beam beam;

        public BeamMoveRunnable(Beam beam) {
            this.beam = beam;
        }

        public void run() {
            Beam beam = this.beam;
            beams.add(beam);
            try {
                while (true) {
                    if (beam.getY() <= 0) {
                        beams.remove(beam);
                        break;
                    }
                    beam.move();
                    repaint();
                    Thread.sleep(20);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private class MouseMotionHandler extends MouseAdapter {
        public void mouseMoved(MouseEvent event) {
            targetPoint = event.getPoint();
        }
    }
}

【问题讨论】:

  • 向我们展示适当的 src,而不仅仅是错误

标签: java multithreading arraylist


【解决方案1】:

这通常发生在您尝试在迭代时从列表中添加或删除项目时:

for (String s : list) {
    list.add("abc"); //ConcurrentModificationException
}

如果没有看到 MainPanel 类第 50 行左右的代码,很难更具体(参见 stacktrace:at spacecommander.MainPanel.paintComponent(MainPanel.java:50))。

编辑

在您进行编辑后,一个简单的更改是使用线程安全的 CopyOnWriteArrayList 而不是 ArrayList 来保存您的 Beam 对象。

【讨论】:

    【解决方案2】:

    由于您使用多个线程,不同的线程正在尝试修改您的beams arraylist。您可以使用同步块来避免并发修改异常,如下所示

    循环列表

    synchronized (beams) {
        for (Iterator it = beams.iterator(); it.hashNext(); ) {
            Beam beam = (Beam) it.next();
           g2.drawImage(beam.getImage(), (int)beam.getX(), (int)beam.getY(), null);
        }
    }
    

    将项目添加到列表中

    syncronized (beams) {
        beams.add(beam);
    }
    

    从列表中删除项目

    syncronized (beam) {
        list.remove(beam);
    }
    

    【讨论】:

      【解决方案3】:

      正如您所怀疑的,这似乎是一个同步问题。可能在您的绘图代码迭代beams-list 时,BeamMoveRunnable 会同时修改列表(添加或删除梁),并导致 ConcurrentModificationException。就我个人而言,我不会使用单独的线程来移动梁,而是一个简单的循环,它首先更新游戏(一次移动一个所有的梁等),然后重绘屏幕。但是,您也可以只同步对列表的访问,这样一次只有一个线程可以访问它。

      【讨论】:

      • 我会尝试同步,希望它能解决错误:) 谢谢回复
      猜你喜欢
      • 1970-01-01
      • 2016-08-13
      • 1970-01-01
      • 1970-01-01
      • 2014-01-03
      • 1970-01-01
      • 1970-01-01
      • 2021-06-22
      • 2021-08-30
      相关资源
      最近更新 更多