我经常听到批评Swing库中缺乏线程安全性.但是,我不确定我在自己的代码中会做什么可能导致问题:
在什么情况下Swing不是线程安全的事实发挥作用?
我应该积极避免做什么?
永远不要执行长时间运行的任务以响应事件线程上的按钮,事件等.如果您阻止事件线程,整个GUI将完全没有响应,导致真正生气的用户.这就是为什么Swing看起来很慢而且很脆弱的原因.
使用Threads,Executors和SwingWorker来运行不在EDT上的任务(事件调度线程).
不要在EDT之外更新或创建小部件.几乎可以在EDT之外进行的唯一调用就是Component.repaint().使用SwingUtilitis.invokeLater确保在EDT上执行某些代码.
使用EDT调试技术和智能外观(如物质,检查EDT违规)
如果您遵循这些规则,Swing可以制作一些非常有吸引力和响应的GUI
一些非常棒的Swing UI工作的例子:Palantir Technologies.注意:我不为他们工作,只是一个令人敬畏的挥杆的例子.羞耻没有公开演示...他们的博客也很好,稀疏,但很好
这是让我很高兴购买Robinson&Vorobiev关于Swing的书的问题之一.
访问的国家什么java.awt.Component
应该在美国东部时间内运行,有三个例外: 任何具体的记录为线程安全的,如repaint()
,revalidate()
和invalidate()
; UI中尚未实现的任何组件; 以及start()
调用该Applet之前Applet中的任何组件.
特制的线程安全方法非常罕见,通常只需记住那些方法就足够了; 你通常也可以假设没有这样的方法(例如,在SwingWorker中包装重绘调用是完全安全的).
实现意味着该组件可以是一个顶层容器(像的JFrame),其上的任何setVisible(true)
,show()
或pack()
已被调用,或者它已被添加到一个实现组件.这意味着在main()方法中构建UI非常好,正如许多教程示例所做的那样,因为它们不会调用setVisible(true)
顶级容器,直到每个Component都添加到它,配置了字体和边框等.
出于类似的原因,在其init()
方法中构建applet UI是完全安全的,然后start()
在它构建完成后调用.
将Runnables中的后续组件更改包装成发送invokeLater()
后,只需执行几次即可轻松完成.我觉得讨厌的一件事是someTextField.getText()
从另一个线程读取组件的状态(比方说).从技术上讲,这也必须包含在内invokeLater()
; 在实践中,它可以使代码变得难看,并且我经常不打扰,或者我在初始事件处理时(在大多数情况下通常是在适当的时候执行它)时小心地抓住该信息.
这不仅仅是Swing不是线程安全的(不是很多),但它是线程敌对的.如果你开始在一个线程(除了EDT)上做Swing的东西,那么当Swing切换到EDT(没有记录)的情况下,可能存在线程安全问题.即使是针对线程安全的Swing文本也没有用于线程安全(例如,要附加到文档,首先需要找到长度,这可能会在插入之前发生变化).
所以,在EDT上做所有Swing操作.注意EDT不是调用main的线程,所以启动你的(简单)Swing应用程序,比如这个样板:
class MyApp { public static void main(String[] args) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { runEDT(); }}); } private static void runEDT() { assert java.awt.EventQueue.isDispatchThread(); ...