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