【问题标题】:ConcurrentModificationException although secure removeIF usedConcurrentModificationException 尽管使用了安全 removeIF
【发布时间】:2018-08-20 09:24:09
【问题描述】:

在从列表中删除一些对象后尝试重新绘制对象列表时发生异常。通过安全的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 方法,在删除时不更改对象。

另外,THheads 和 TTails 不继承自任何 Swing 超类,因此它们各自的 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;
        }
    }
}

除了图形对象本身之外,它是操作过程中唯一被操纵的对象。

也许我需要让TPanelObjectJComponent 继承并将对象添加到面板本身。异常可能是由对 Graphics 对象的访问引起的,因为它在重绘时不再有效?

【问题讨论】:

  • 尝试TTail tTail : new ArrayList( tails )对集合的副本进行迭代,避免在对集合进行迭代时修改集合引起的异常。

标签: java list loops jpanel


【解决方案1】:

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);
    }
}

【讨论】:

  • paint-方法是对 tTail 对象的唯一外部访问。只有tick()paint() 他们执行内部操作。但他们不会互相访问。
  • @TorhanBartel 你的tick()paintComponent() 方法在不同的线程中运行。如果tick()paintComponent() 迭代集合时删除了一个元素,您将得到ConcurrentModifcationException。绘画是异步的,你无法控制它何时真正发生。调用repaint() 只是请求重新绘制。
  • @TorhanBartel 您还应该从堆栈跟踪中看到哪个线程正在抛出异常。我向甜甜圈打赌,这是 AWT 事件线程。
  • 好的,完成了。检查我的编辑。是的,它是 AWT 线程:D
  • @TorhanBartel 你同步错了。您应该同步访问tails。当我告诉你该怎么做时,你正在发明各种疯狂的理论。参见编辑示例代码。
猜你喜欢
  • 2010-12-11
  • 2018-05-18
  • 2019-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多