为什么我在迭代时没有删除就得到当前修改异常?- Java
Why am i getting a CurrentModificationException without removing while iterating?- Java
我没有使用线程,但我一直收到一条错误消息:
"Exception in thread "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);
}
}
}
还有一个来自不同 class 的方法在非常接近 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();
}
}
}
}
ConcurrentModificationException 与线程无关。
它的意思是:我们正处于以下时间轴的 'Z' 点:
- 在 X 点,您从集合中获取了一个迭代器。
- 在 Y 点(X 之后),集合被某人修改(可以在另一个线程中,也可以在您自己的线程中,发生在何处并不重要)。
- 在 Z 点(Y 之后),您调用 X 中获得的迭代器上的任何操作。
请注意,您在进入 for (Type elem : someCollection)
块时创建了一个迭代器,并且在 for 循环循环的任何时候都对该迭代器调用 next
操作。
从您的代码中无法判断修改发生的位置(您没有这样做;在 for 循环中像 collection.remove(x) 这样简单的事情可能会导致 ConcurrentModificationException
,但您在粘贴的代码段中没有这样做)。
请注意,使用 swing,您可以拥有多个线程,即使您认为自己没有。因此,如果该片段确实是您所拥有的全部,则涉及另一个线程并且他们正在修改 shapes
或 shapes
.
中的一项。
问题是您正在使用多线程,即使您没有意识到:
- 你的主线程
- Swing UI 线程
Swing(以及那里的大多数 GUI 框架)是单线程的,因为只有一个线程应该操作 GUI 和 G[=51= 所在的任何数据] 视情况而定。
参见:Why is Swing threading model considered wrong and how should it be?
repaint
方法不会立即绘制;它在事件队列中放置一个事件,当 Swing 线程拾取该事件时,它就会执行您的重绘代码。
在您尚未发布的 tick
方法中,您正在更新您在 paint
方法中迭代的相同列表,并且因为 paint
方法有时 运行 与您的 tick
方法同时发生,这会导致 ConcurrentModificationExceptions。
有几种解决方法。
- 您可以使用双缓冲,让您的主线程更新不可见的缓冲区,并让重绘方法在准备就绪时显示缓冲区。
- 您可以 运行 Swing 线程上的所有内容,使用
EventQueue.invokeLater
或 javax.swing.Timer
(您可以这样安排您的 tick
方法)
- 您可以在
paint
方法中复制列表,然后再对其进行迭代。但是,由于您使用的是多线程,除非您采取特别的预防措施,否则可能会导致数据争用。您可以使用 CopyOnWriteArrayList
而不是普通的 ArrayList
,或者使用 Collections.synchronizedList
将普通列表变成一个安全地将一个线程中的更改发布到另一个线程的列表。
我没有使用线程,但我一直收到一条错误消息:
"Exception in thread "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);
}
}
}
还有一个来自不同 class 的方法在非常接近 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();
}
}
}
}
ConcurrentModificationException 与线程无关。
它的意思是:我们正处于以下时间轴的 'Z' 点:
- 在 X 点,您从集合中获取了一个迭代器。
- 在 Y 点(X 之后),集合被某人修改(可以在另一个线程中,也可以在您自己的线程中,发生在何处并不重要)。
- 在 Z 点(Y 之后),您调用 X 中获得的迭代器上的任何操作。
请注意,您在进入 for (Type elem : someCollection)
块时创建了一个迭代器,并且在 for 循环循环的任何时候都对该迭代器调用 next
操作。
从您的代码中无法判断修改发生的位置(您没有这样做;在 for 循环中像 collection.remove(x) 这样简单的事情可能会导致 ConcurrentModificationException
,但您在粘贴的代码段中没有这样做)。
请注意,使用 swing,您可以拥有多个线程,即使您认为自己没有。因此,如果该片段确实是您所拥有的全部,则涉及另一个线程并且他们正在修改 shapes
或 shapes
.
问题是您正在使用多线程,即使您没有意识到:
- 你的主线程
- Swing UI 线程
Swing(以及那里的大多数 GUI 框架)是单线程的,因为只有一个线程应该操作 GUI 和 G[=51= 所在的任何数据] 视情况而定。
参见:Why is Swing threading model considered wrong and how should it be?
repaint
方法不会立即绘制;它在事件队列中放置一个事件,当 Swing 线程拾取该事件时,它就会执行您的重绘代码。
在您尚未发布的 tick
方法中,您正在更新您在 paint
方法中迭代的相同列表,并且因为 paint
方法有时 运行 与您的 tick
方法同时发生,这会导致 ConcurrentModificationExceptions。
有几种解决方法。
- 您可以使用双缓冲,让您的主线程更新不可见的缓冲区,并让重绘方法在准备就绪时显示缓冲区。
- 您可以 运行 Swing 线程上的所有内容,使用
EventQueue.invokeLater
或javax.swing.Timer
(您可以这样安排您的tick
方法) - 您可以在
paint
方法中复制列表,然后再对其进行迭代。但是,由于您使用的是多线程,除非您采取特别的预防措施,否则可能会导致数据争用。您可以使用CopyOnWriteArrayList
而不是普通的ArrayList
,或者使用Collections.synchronizedList
将普通列表变成一个安全地将一个线程中的更改发布到另一个线程的列表。