我有一个程序需要在另一个帖子上更新JList
的内容DefaultListModel
。由于内容的数量可能会不时变化,因此我只需清除所有内容,并在更新时将新内容添加到DefaultListModel
。但似乎我遇到了JFrame
在我的线程正在进行更新时开始刷新的问题。我有这样的例外
Exception in thread "AWT-EventQueue-0"
java.lang.ArrayIndexOutOfBoundsException: 3
以下是代码示例
DefaultListModel model;
JList jList;
JScrollPane jScrollPane;
Thread thread;
public Frame() {
this.setTitle("ASM_SIMULATOR");
this.setBounds(100, 100, 500, 500);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.getContentPane().setLayout(null);
model = new DefaultListModel();
jList = new JList(model);
jScrollPane = new JScrollPane(jList);
jList.setBounds(50, 50, 300, 200);
jScrollPane.setBounds(50, 50, 300, 200);
this.getContentPane().add(jScrollPane);
this.setVisible(true);
thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
makeData();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.start();
}
public void makeData() {
System.out.println("makeData()");
model.clear();
for (int i = 0; i < 20; i++) {
model.addElement((int) (Math.random() * 100));
}
}
public static void main(String[] args) {
new Frame();
}
答案 0 :(得分:3)
基本答案是
Swing不是线程安全的。
您需要做的是使用SwingWorker
构建模型并使用其done
/ process
方法将其应用回视图或使用SwingUtilities.invokeLater
继续使用您的线程,但将更新同步回事件调度线程
答案 1 :(得分:3)
你违反了基本的“所有Swing组件应该在Event Dispatch Thread(= EDT)上访问/修改,而在EDT上只有”两次在该代码片段中。
new Frame()
调用包裹在SwingUtilities#invokeLater
或类似方法中JList
收到的JList
更新自身的事件(再次,在错误的线程上)。两种可能的解决方案:
DefaultListModel
,并在EDT上一次性替换它。答案 2 :(得分:2)
必须将model.addElement((int) (Math.random() * 100));
换入invokeLater
正确的方法可能是Thread
的{{1}}启动工作人员,或使用Runnable#Thread
来自SwingWorker
的方法SwingWorker
和publish()
的输出可以在process()
上进行
答案 3 :(得分:0)
不幸的是,事情并非那么简单。只允许GUI线程更新GUI,因此任何其他线程都需要通过SwingUtilities.InvokeLater
将任何更新转发到GUI线程。在您的情况下,您可以只包装整个makeData
方法,因为它只是更新GUI:
thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
SwingUtilities.InvokeLater(new Runnable() {
public void run() {
makeData();
}
});
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
请注意,现在makeData
的代码将在GUI线程上执行。在其他情况下,当您正在进行其他不涉及GUI的耗时工作时,您应该以更精细的方式使用InvokeLater
来保持UI线程尽可能免费。
编辑:仔细查看代码,我注意到您所做的只是每200毫秒定时更新一次GUI。使用javax.swing.Timer
:
int delay = 200; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
makeData();
}
};
new Timer(delay, taskPerformer).start();