[英]updating DefaultListModel on another thread
我有一个程序需要更新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();
}
基本答案是不要
Swing不是线程安全的。
您需要做的是使用SwingWorker
构建模型并使用其done
/ process
方法将其应用回视图或使用SwingUtilities.invokeLater
继续使用您的线程,但将更新同步回Event Dispatching Thread
有关详细信息,请阅读Swing中的Concurrency
你违反了基本的“所有Swing组件应该在Event Dispatch Thread(= EDT)上访问/修改,而在EDT上只有”两次在该代码片段中。
SwingUtilities#invokeLater
或类似的方法中包装new Frame()
调用 JList
,对其中的JList
自我更新(再次,错误的线程上)。 两种可能的解决方
DefaultListModel
,并在EDT上一次性替换它。 你有Swing中的并发问题
必须包装model.addElement((int) (Math.random() * 100));
进入invokeLater
正确的方法可以开始工作者Thread
从Runnable#Thread
,或使用SwingWorker
SwingWorker
方法的输出可以在EDT
上执行publish()
和process()
不幸的是,事情并非那么简单。 只允许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();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.