为什么我在迭代时不删除就得到CurrentModificationException?-Java

时间:2018-09-08 23:02:57

标签: java arrays exception jframe concurrentmodification

我没有使用线程,但是我不断收到错误消息:

“线程“ AWT-EventQueue-0”中的异常java.util.ConcurrentModificationException”

这是我的代码:

@Override
public void paint(Graphics g) {
    for(Rectangles emp: shapes.list) {
        //Loop through all rectangle objects
        for(int[] temp: emp.arr) {
            //Loop through each objects array
            g.drawRect(temp[0], temp[1], 20, 20);
    g.drawRect(20, 20, 20, 20);
        }
    }
}

还有一个来自不同类的方法,该方法非常接近paint方法,一遍又一遍地执行,这可能会引起问题。

public class Shapes {
LinkedList<Rectangles> list  = new LinkedList<Rectangles>();
Random rand = new Random();
void newshape() {
    int shape = rand.nextInt(7);
    switch(shape) {
        case 0:
            list.add(makeSquare());
            break;
        case 1:
            list.add(makeLine());
            break;
        case 2:
            list.add(makeTShape());
            break;
        case 3:
            list.add(makeLShape());
            break;
        case 4:
            list.add(makeJShape());
            break;
        case 5:
            list.add(makeZShape());
            break;
        case 6:
            list.add(makeSShape());
            break;
        }
    }

我正在使用Notch游戏循环,如果自上次制作新形状以来已过一秒钟,则tick()方法将调用newshape()方法。然后调用paint方法。

public void run(Game game) {
    while(true) {
        lastshapemake = System.nanoTime();
        long lastTime = System.nanoTime();
        double Target_FPS = 60.0;
        double ns = 1000000000 / Target_FPS;
        double delta = 0;
        while(running) { 
            long now = System.nanoTime();
            delta += (now - lastTime) / ns;
            lastTime = now;
            while(delta >= 1) {
                tick();
                delta--;

            }
            if(running) {
                game.repaint();
            }
        }   
    }
}

2 个答案:

答案 0 :(得分:2)

问题是,即使您没有意识到,您也正在使用多个线程:

  1. 您的主线程
  2. Swing UI线程

Swing(以及其中的大多数GUI框架)都是单线程的,这意味着只有一个线程可以操作GUI以及该GUI依赖的任何数据。

请参阅:Why is Swing threading model considered wrong and how should it be?

AggregateIterable<Document> findIterable = tripsCollection.aggregate( Arrays.asList( new Document("$match", new Document("busId", busId)), new Document("$unwind", "$location"), new Document("$sort", new Document("location.position", -1)), new Document("$group", new Document("_id", "$_id") .append("driverId", new Document("$first","$driverId")) .append("busId", new Document("$first","$busId")) .append("startTime", new Document("$first","$startTime")) .append("location", new Document("$push","$location")) ), new Document("$sort", new Document("startTime", -1)) ) ); 方法不会立即绘制;它将一个事件放到事件队列中,当该事件被Swing线程拾取后,它将执行您的重绘代码。

在您尚未发布的repaint方法期间,您正在更新要在tick方法中迭代的相同列表,并且由于paint方法有时会运行与您的paint方法完全同时,这会导致ConcurrentModificationExceptions。

有两种解决方法。

  1. 您可以使用双缓冲,让您的主线程更新不可见的缓冲区,并让repaint方法在就绪后显示该缓冲区。
  2. 您可以使用tickEventQueue.invokeLater在Swing线程上运行所有内容(可以这样安排javax.swing.Timer方法)
  3. 您可以在迭代之前通过tick方法复制列表。但是,由于使用多个线程,除非采取特殊预防措施,否则可能导致数据争用。您可以使用paint代替普通的CopyOnWriteArrayList,也可以使用ArrayList将普通列表变成一个列表,然后将一个线程中的更改安全地发布到另一个线程中。

答案 1 :(得分:0)

ConcurrentModificationException与线程无关。

这意味着:在以下时间轴上,我们位于点'Z':

  • 在X点,您从集合中获取了一个迭代器。
  • 在Y点(X之后),集合被某人修改了(可以在另一个线程中,也可以在您自己的线程中,它发生在哪里都没有关系)。
  • 在Z点(Y之后),您对X中获得的迭代器调用任何操作。

请注意,您需要在进入for (Type elem : someCollection)块后创建一个迭代器,并在每次循环循环时都对该迭代器调用next操作。

从您的代码中无法得知修改发生的位置(您没有这样做;在for循环中像collection.remove(x)这样简单的事情可能会导致ConcurrentModificationException,但您不要在您粘贴的代码段中执行此操作。

请注意,即使您认为自己做不到,也可以使用swing创建多个线程。因此,如果该代码段确实是您所拥有的,那么将涉及另一个线程,并且它们正在修改shapesshapes中的一项。