嗨我正在写一个非常简单的游戏。玩家可以使用鼠标移动宇宙飞船,每200ms新射束射击。这个光束在(真)循环中移动,当它的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();
}
}
}
答案 0 :(得分:2)
这似乎是一个同步问题,正如您所怀疑的那样。可能在您的绘图代码迭代beams
- 列表时,BeamMoveRunnable
同时修改列表(添加或删除波束),并导致 ConcurrentModificationException 。我个人不会使用单独的线程移动光束,而是一个简单的循环,首先更新游戏(一次移动所有光束等),然后重绘屏幕。但是,您也可以只是同步对列表的访问,这样一次只有一个线程可以访问它。
答案 1 :(得分:1)
当您尝试在迭代时添加或删除列表中的项目时,通常会发生这种情况:
for (String s : list) {
list.add("abc"); //ConcurrentModificationException
}
如果没有看到MainPanel类第50行附近的代码(参见stacktrace:at spacecommander.MainPanel.paintComponent(MainPanel.java:50)
),很难更具体。
修改强>
在编辑之后,一个简单的改变就是使用一个线程安全的CopyOnWriteArrayList来代替ArrayList来保存你的Beam对象。
答案 2 :(得分:1)
由于您使用多个线程,因此不同的线程正在尝试修改您的beams arraylist
。您可以使用synchronized块来避免concurent修改异常,如下所示
循环列表
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);
}