为了维护安全,某一个任务必须运行在Swing的事件线程中。但是你不能在事件线程中执行耗时操作,以免UI失去响应。而且Swing的数据结构不是线程安全的所以你必须小心的把他们限制在事件线程中。
几乎所有的GUI工具集都是单线程化的子系统,意味着所有GUI的活动都被限制在一个单独的线程中。
一.为什么GUI是单线程化的
现代的GUI框架使用了一个模型:模型创建了一个专门的事件发派线程来处理GUI事件。多线程的GUI框架尤其容易造成死锁的影响。
1.顺序处理事件
因为只有唯一一个线程在处理GUI任务,所以他会依次处理GUI任务-一个任务完成后才开始下一个。顺序任务不利的一面是如果一个任务执行要花费的时间长,其他任务也要等到它结束。
2.Swing中的线程限制
所有的Swing组件(比如Jbutton)和数据模型(TableModel)都被限制于事件线程中。GUI对象不用同步,仅仅靠线程限制来保持一致性。这样有利于运行事件的任务不必担心同步的问题;但是你无法从事件之外的地方访问表现对象。
Swing的单线程规则:Swing的组件和模型只能在事件分派的线程中被创建、修改和请求。
单线程规则中还有一些特例,它们可以安全的被任意线程调用:
- SwingUtilities.isEventDispatchThread,他用来判断当前线程是否是事件线程。
- SwingUtilities.invokeLater,它可以安排一个Runnable任务在事件线程中执行(可以从任意线程调用)。
- SwingUtities.invokeAndWait,它可以安排一个Runnable任务在事件线程中执行,并且会阻塞当前线程直到它完成(只能在非GUI线程中调用)
- 用于将一个repaint或revalidation请求插入到队列的方法集(可以从任意线程调用)
- 最后用于添加或移除监听器的方法集(可以被任意线程调用,但是监听器一定在事件线程中调用)
Swing可以看成一个单线程化的Executor,它执行事件队中的任务,与线程池一样,有时一个线程死亡的工作者会被另一个新的取代。
二.短期GUI任务
为了简单起见,短期任务可以把全部动作留在时间线程中完成,对于耗时的任务应该将一些工作复核分压到另一个线程中。
三.耗时GUI任务
成熟的GUI应用可能会执行耗时的任务。这些任务必须在另外的线程中运行,而使GUI在它们运行中可以做出响应。
1.取消
任何一个在线程中运行了足够长时间的任务都可能消耗用户大量时间,因此用户可能希望取消它。你可以使用线程中断进行取消,不过更简单的办法是使用Future。
2.进度完成表示符
FutureTask中有一个done钩子函数,可以方便任务完成后的通知,后台Callable完成后会调用done。
3.SwingWorker
在Swing中,取消息、完成消息通知、以及进度指示都是由SwingWorker类提供的、
四.共享数据类型
Swing表现对象(TableModel、TreeModel这些数据模型)是被限制在事件线程中的。在简单的GUI程序中、所有的可变对象都保存在表现对象中,事件线程之外唯一的线程就是主线程。强制这些遵守单线程的规则很容易:不要在主线程中访问数据模型或表现组件。