在某些对象已从列表中删除后尝试重新绘制对象列表时,会发生异常。项目通过安全的 Collections.removeIf(...)
方法删除。异常发生在 JPanels repaint()
链中。
重要方法:
public void run() {
isRunning = true;
while (isRunning) {
try {
tick();
repaint();
Thread.sleep(20);
} catch (Exception e) {
isRunning = false;
}
}
}
勾选和重新绘制对象分为不同的方法,每个方法都包含循环。
private void tick() {
for (THead head : heads) {
head.tick();
}
for (TTail tTail : tails) {
tTail.tick();
}
tail.removeIf(o -> !o.isAlive());
}
如果不再设置 alive
标志,TTails 可能会被删除。
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (TTail tTail : tails) {
tTail.paint(g2d);
}
for (THead head : heads) {
head.paint(g2d);
}
}
勾选后,所有对象都将被重新绘制。这就是 ConcurrentModificationException 发生的地方。我不明白为什么。它全部在同一个线程中运行,并且没有使用嵌套循环。要删除项目,我使用 removeIf
方法,在删除时不会更改对象。
此外,THeaads 和 TTails 并不继承自任何 Swing 父类(super class),因此它们各自的 paint
方法仅使用图形对象来执行绘制操作,而不会破坏绘制链。
编辑1:
我现在已同步访问 pos 对象,该对象可在 paint()
和 tick()
中访问。
public abstract class TPanelObject {
private Point pos;
private Object lock = new Object();
public Point getPos() {
synchronized (lock) {
return pos;
}
}
public void setPos(Point pos) {
synchronized (lock) {
this.pos = pos;
}
}
}
除了图形对象本身之外,它是操作期间唯一操纵的对象。
也许我需要让TPanelObject
继承JComponent
并将对象添加到面板本身。异常可能是由访问 Graphics 对象引起的,因为它在重绘时不再有效?
最佳答案
Repaint/paintComponent 等在事件线程中运行,您的 run()
方法和 tick()
方法在不同的线程中运行,因此您确实有多个线程访问相同的数据。
遗憾的是,您需要同步对 tails
的访问(或者以其他方式确保共享数据的访问安全)。
¹ 您当然可以从线程中调用它们,但 EDT 将在通过 repaint()
请求时执行绘制。
private void tick() {
for (THead head : heads) {
head.tick();
}
for (TTail tTail : tails) {
tTail.tick();
}
synchronized(tails) {
tail.removeIf(o -> !o.isAlive());
}
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
synchronized(tails) {
for (TTail tTail : tails) {
tTail.paint(g2d);
}
}
for (THead head : heads) {
head.paint(g2d);
}
}
关于java - 尽管使用了安全removeIF,但出现了ConcurrentModificationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51927684/