迭代器和ConcurrentModificationException

时间:2013-09-21 16:04:08

标签: java exception concurrency iterator

我有一个LinkedList<JLabel>并在一个线程上使用它来从JPanel添加或删除它们,问题是有时异常发生并且应用程序崩溃。在一些帖子中,他们谈论同步我的列表,其他人说我应该有一个迭代器,所以我有点困惑。

我会添加一些代码,以便于理解:

public class MyPanel extends JPanel {

(...)
private LinkedList<JLabel> labels;
(...)

public MyPanel(Color corLabel, Color back, Font text) {
(...)
labels = new LinkedList<>();
(...)
}

这是链接列表初始化的地方,现在我有1个方法和1个线程使用它:

 public void addMensagem(MensagemParaEcra msg) {

    JLabel lbl = new JLabel();
    lbl.setText(("- " + msg.getTexto() + " -"));
    lbl.setForeground(color);
    lbl.setFont(textFont);
    lbl.setOpaque(true);
    lbl.setBackground(backg);

    labels.add(lbl);
    MyPanel.this.add(lbl);

}

这个只需配置标签并将其添加到面板和链接列表

private void start() {
    final Runnable running = new Runnable() {
        @Override
        public void run() {
            MyPanel.this.repaint();

        }
    };

    Thread t = new Thread() {
        public void run() {
            while (true) {
                // for each label
                for (JLabel lb : labels) {
                    if (lb.getLocation().x + lb.getWidth() < 0) {
                        if (msgsRemover.isEmpty() == true) {
                            //remove the label if she is out of the monitor
                            MyPanel.this.remove(lb);
                            // and add the label
                            MyPanel.this.add(lb);

                            MyPanel.this.repaint();
                            MyPanel.this.validate();
                        } else {
                            // if there is some message to remove
                            for (String s : msgsRemover) {
                                if (lb.getText().toString().equals(s)) {
                                    // remove the visual label   
                                    MyPanel.this.remove(lb);
                                    MyPanel.this.repaint();
                                    // remove the message that need to be removed from the linked list
                                    msgsRemover.remove(s);
                                    // remove the label from the JLabel list
                                    labels.remove(lb);

                                } else {
                                    // if there is no message to be removed, they will just continue
                                    // going to the end of the queue
                                    MyPanel.this.remove(lb);
                                    MyPanel.this.add(lb);

                                    MyPanel.this.repaint();
                                    MyPanel.this.validate();
                                }
                            }

                        }

                    }
                    lb.setLocation(lb.getLocation().x - 3, 0);

                }
                repaint();
                try {
                    SwingUtilities.invokeAndWait(running);
                    sleep(30);
                } catch (InterruptedException ex) {
                    Logger.getLogger(MyPanel.class.getName()).log(Level.SEVERE, null, ex);
                } catch (InvocationTargetException ex) {
                    Logger.getLogger(MyPanel.class.getName()).log(Level.SEVERE, null, ex);
                }

            }
        }
    };
    t.start();
}

这是我收到错误的代码,有时程序会崩溃,问题是什么:

for (JLabel lb : labels)

stacktrace:

Exception in thread "Thread-2" java.util.ConcurrentModificationException
at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:953)
at java.util.LinkedList$ListItr.next(LinkedList.java:886)
at smstest.MyPanel$3.run(MyPanel.java:110)

所以,我使用了迭代器并且它正在工作,但现在我面临另一个问题...... 有代码:

for(Iterator<String> it2 = msgsRemover.iterator(); it.hasNext();){
                                String s = it2.next();

                                if (lb.getText().toString().equals(s)) {
                                    // remove the visual label   
                                    MyPanel.this.remove(lb);
                                    MyPanel.this.repaint();
                                    // remove the message that need to be removed from the linked list
                                    it2.remove();
                                    // remove the label from the JLabel list
                                    it.remove();

所以,现在我的问题是在String s = it2.next(); 堆栈跟踪是:

Exception in thread "Thread-2" java.util.NoSuchElementException
at java.util.LinkedList$ListItr.next(LinkedList.java:888)
at smstest.MyPanel$3.run(MyPanel.java:131)

任何线索如何解决? 提前谢谢

3 个答案:

答案 0 :(得分:3)

在迭代时,您必须使用IteratorList中删除:

final Iterator<JLabel> labelIter = labels.iterator();
while(labelIter.hasNext()) {
    final JLabel label = labelIter.next();
    //do stuff with label
    labelIter.remove();
}

更重要的是,你不能做你想做的事。 Swing不是线程安全的。

您无法更改摆脱EDT的组件。

请在继续之前阅读this。否则,您将最终遇到种族危险和看似随机的GUI问题。

答案 1 :(得分:0)

如果在迭代时修改了数据结构(添加或删除内容),则迭代器会抛出ConcurrentModificationException

要解决此问题,您可以克隆标签结构并迭代克隆。

答案 2 :(得分:0)

你不能在多线程上下文中使用LinkedList而是使用java.util.concurrent.CopyOnWriteArryayList而不是