background image

  Swing 单线线程规则指定:

  Swing 组件和模块只应当从事件分派线程中创建、修改和实现。

 

  单线程规则的早期描述允许 Swing 组件和模块在屏幕上出现之前,由其他线程

访 问 , 但 是 这 种 方 式 带 来 了 线 程 安 全 问 题 , 所 以 规 则 被 强 化 了 。 使 用 
SwingUtilities.isEventDispatchThread() 方法,Swing 

 “

提供了一个机制,询问 当前线程是不

是事件分派线程?”

 

。所以需要在每个 Swing 的方法调用之前插入代码,检查调用是否是由

 

合适的线程发出的,如果不是由合适线程发出的,就抛出 AssertionError,这样就能在测
试中捕捉到对单线程规则的违犯,而不会让它们在生产中造成莫名其妙的故障。

 

  清单 3 演示了一个可以检测许多单线程规则违犯的方面:它有两部分:不应当

从事件线程之外调用的方法的列表,以及要插到对这些方法的调用之前的代码。建议 (要
插 入 的 代 码 ) 非 常 简 单 : 检 查 当 前 线 程 是 否 是 事 件 线 程 , 如 果 不 是 , 就 抛 出 
AssertionError

 

。这个方面处理了对 Swing 

 

包中的所有方法以及扩展了最重要的 Swing 类的

那些类中的方法的全部调用(以便捕捉用户提供的组件和模块),但是它排除了这些类中
已知为可以安全地(或者需要)从多线程调用的方法。安全方法列表并不全面;构建一个全面

 

的列表可能要花费一些额外时间研究 Javadoc,找到所有标记为线程安全的方法。

 

  清单 3. 

 

实施 Swing 的单线程规则的方面

1
2 public aspect SwingThreadAspect {
3
4 pointcut swingMethods() : call(* javax.swing..*.*(..))
5         || call(javax.swing..*.new(..));
6
7 pointcut extendsSwing() : call(* javax.swing.JComponent+.*(..))
8         || call(* javax.swing..*Model+.*(..))
9         || call(* javax.swing.text.Document+.*(..));
10
11 pointcut safeMethods() : call(void JComponent.revalidate())
12         || call(void JComponent.invalidate(..))
13         || call(void JComponent.repaint(..))
14         || call(void add*Listener(EventListener+))
15         || call(void remove*Listener(EventListener+))
16         || call(boolean SwingUtilities.isEventDispatchThread())
17         || call(void SwingUtilities.invokeLater(Runnable))
18         || call(void SwingUtilities.invokeAndWait(Runnable))
19         || call(void JTextPane.replaceSelection(..))
20         || call(void JTextPane.insertComponent(..))
21         || call(void JTextPane.insertIcon(..))