我有一个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)
任何线索如何解决? 提前谢谢
答案 0 :(得分:3)
在迭代时,您必须使用Iterator
从List
中删除:
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而不是