在基于Java Swing进行图形界面开发的时候经常遇到的就是Swing多线程问题我们可以想想一下如果需要在一个图形界面上显示很多数据这些数据是经过长时间复杂的查询和运算得到的如果在图形界面的同一个线程中进行查询和运算工作则会导致一段时间界面处于死机状态这会给用户带来不良的互动感受为了解决这个问题一般会单独启动一个线程进行运算和查询工作并随时更新图形界面这时候另一个问题就出现了可能不仅没有解决原来偶尔死机问题还可能导致程序彻底死掉幸运的是在JDK中暗藏了一个中断程序的快捷键就是CTRL+BREAK这个快捷键Sun并没有在文档中公布如果在命令行模式下启动Java程序然后按CTRL+BREAK键会得到堆栈的跟蹤信息从这些跟蹤信息中就可以知道具体引发死机的位置了
当一个程序产生死锁的时候你一定会希望尽快找到原因并且解决它这时候你一般的精力会用在查找引发死锁的位置另一半的精力会用于对堆栈进行跟蹤一确定引发死锁的原因但是在Java Swing程序中你的所有努力可能都是没有价值的这是因为Java对Swing的多线程编程有一个特殊要求就是在Swing里只能在与Swing相同的线程里对GUI元件进行修改
也就是说如果你要执行类似于jLabelsetText(blabla)代码必须在Swing线程中而不允许在其他线程当中如果必须在其他线程中修改元件可以使用类似一下方式解决
SwingUtilitiesinvokeLater(new Runnable() {
public void run() {
jLabelsetText(blabla);
}
}
invokeLater方法虽然表面有时间延迟执行含义但是实际上几乎没有任何影响可能在几毫秒之内就会被执行另外还有一个invokeAndWait方法除非特殊需要否则几乎是不用的
在不使用invokeLater的情况下导致刷新问题是可以理解的但是导致死锁就优点令人匪夷所思了幸运的是不是任何时候都需要调用改方法这是因为大多数情况下我们都是在与Swing同一个线程里进行界面更新例如监听按钮点击事件的ActionListeneractionPerformed方法就是运行在与Swing相同的线程中的但是如果在回调类中引用了另一个类并且是不属于AWT/Swing的那么结果就很难确定了所以说使用invokeLater应该是最安全的
需要注意的是在invokeLater做的任何事情都会导致Swing线程窗口绘制工作暂停下来等候invokeLater工作结束所以不要在invokeLater进行耗时操作尽量只执行那些界面绘制相关的工作可以通过代码重构将那些与界面更新相关的代码集中起来统一处理
一个建议是那些在Swing中使用的类进行合理的设计代码执行前判断是否处于Swing线程当中(使用SwingUtilitiesisEventDispatchThread()方法)如果不是则需要通过SwingUtilitiesinvokeLater(Runnable)执行否则则直接执行代码但是这说起来简单但是实际操作会遇到很多困难