在Java中Swing是线程不安全的,是单线程的设计,这样的造成结果就是:只 能从事件派发线程访问将要在屏幕上绘制的Swing组件。事件派发线程是调用 paint和update等回调方法的线程,它还是事件监听器接口中定义的事件处理方 法,例如,ActionListener中的actionPerformed方法在事件派发线程中调用。
Swing是事件驱动的,所以在回调函数中更新可见的GUI是很自然的事情,比 如,有一个按钮被按下,项目列表需要更新时,则通常在与该按钮相关联的事件 监听器的actionPerformed方法中来实现该列表的更新,从事件派发线程以外的 线程中更新Swing组件是不正常的。
有时需要从事件派发线程以外的线程中更新Swing组件,例如,在 actionPerformed中有很费时的操作,需要很长时间才能返回,按钮激活后需要 很长时间才能看到更新的列表,按钮会长时间保持按下的状态只到 actionPerformed返回,一般说来耗时的操作不应该在事件处理方法中执行,因 为事件处理返回之前,其他事件是不能触发的,界面类似于卡住的状况,所以在 独立的线程上执行比较耗时的操作可能更好,这会立即更新用户界面和释放事件 派发线程去派发其他的事件。
SwingUtilities类提供了两个方法:invokeLate和invoteAndWait,它们都使 事件派发线程上的可运行对象排队。当可运行对象排在事件派发队列的队首时, 就调用其run方法。其效果是允许事件派发线程调用另一个线程中的任意一个代 码块。
只有从事件派发线程才能更新组件。
程序示例:更新组件的错误方法
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
GetInfoThread t = new GetInfoThread(Test.this);
t.start();
startButton.setEnabled(false);
}
});
class GetInfoThread extends Thread {
Test applet;
public GetInfoThread(Test applet) {
this.applet = applet;
}
public void run() {
while (true) {
try {
Thread.sleep(500);
applet.getProgressBar().setValue(Math.random() * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}