Swing 的线程策略
总结一句话:对Swing所有组件和model(模型)的更改 必须至于EDT 事件派发线程中.
通常 Swing 不是线程安全的。除非另行说明,否则所有 Swing 组件及相关类都必须在事件调度线程上访问。
典型的 Swing 应用程序执行处理以响应用户动作所生成的事件。例如,单击 JButton
通知所有添加到 JButton
的 ActionListener
。由于用户动作所生成的所有事件都在调度线程上指派,所以大部分开发人员不受该限制的影响。
但是,影响存在于构造以及显示 Swing 的应用程序中。对应用程序的 main
方法或 Applet
中方法的调用不在事件调度线程上调用。因此,构造和显示应用程序或 applet 时,必须注意要将控件转移到事件调度线程。转移控件和开始处理 Swing 的首选方法是使用 invokeLater
。invokeLater
方法安排 Runnable
在事件调度线程上处理。以下两个示例都同样很好地用于转移控件和启动 Swing 应用程序:
public class MyApp implements Runnable { public void run() { // Invoked on the event dispatching thread. // Construct and show GUI. } public static void main(String[] args) { SwingUtilities.invokeLater(new MyApp(args)); } }
或:
public class MyApp { MyApp(String[] args) { // Invoked on the event dispatching thread. Do any initialization // here. } public void show() { // Show the UI. } public static void main(final String[] args) { // Schedule a job for the event-dispatching thread: // creating and showing this application's GUI. SwingUtilities.invokeLater(new Runnable() { public void run() { new MyApp(args).show(); } }); } }
此限制也适用于连接到 Swing 组件的模型。例如,如果将 TableModel
连接到 JTable
,则 TableModel
应该只在事件调度线程上进行修改。如果修改单独线程上的模型,就有遭遇异常和可能的显示损坏的风险。
由于所有事件都是在事件调度线程上传递的,所以必须注意事件处理。尤其是,在事件调度线程上执行的长运行时间任务(如网络 io 或计算密集处理)将阻塞事件调度线程调度任何其他线程。事件调度线程受阻塞时,应用程序对用户输入完全没有反应。有关处理 Swing 时执行这种处理的首选方式,请参阅 SwingWorker