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(..))