6 Swing 事件分發線程(EDT)
Swing的事件隊列就類似事件隊列,僅單一消費者,即一個事件分發線程。
除非你的程式停止,否則EDT會永不間斷地徘徊在處理請求與等待請求之間。
Swing事件隊列的實作機制圖解
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5CMiZDMmdTMxEDO0QDN4ITY1EjY0YmNykzN5gjZzQTZy8CX5d2bs92Yl1iclB3bsVmdlR2LcNWaw9CXt92Yu4GZjlGbh5yYjV3Lc9CX6MHc0RHaiojIsJye.png)
6.1 單一線程的事件隊列的特性
- 将同步操作轉為異步操作
- 将并行處理轉換為串行順序處理
6.2 EDT要處理所有GUI操作
- 職責明确,任何GUI請求都應該在EDT中調用
- 要處理的GUI請求非常多,包括視窗移動、元件自動重繪、重新整理,它很忙。任何與GUI無關的處理不要由EDT執行,尤其是I/O耗時操作
7 Swing不是一個“安全線程”的API,為什麼要這樣設計
Swing的線程安全不是靠自身元件的API來保障,雖然repaint方法是這樣,但是大多數SwingAPI是非線程安全的,也就是說不能在任意地方調用,它應該隻在EDT中調用。Swing的線程安全靠事件隊列和EDT保證。
8 invoke系方法
對非EDT的并發調用需通過invokeLater()和invokeAndWait()使請求插入到隊列中等待EDT去執行。
由于Swing本身非線程安全,如果你在其他線程通路和修改GUI元件,必須使用
8.1 SwingUtilities. invokeAndWait(runnable)
同步的,它被調用結束會立即阻塞目前線程,直到EDT處理完該請求。
一般用于取得Swing元件的資料。
8.2 SwingUtilities. invokeLater(runnable)
使 doRun.run() 在AWT事件分法線程上異步執行。所有待處理的AWT事件被執行後,就會發生這種情況。當應用程式線程需要更新GUI時,應使用此方法。
在下面的示例中,invokeLater調用将Runnable對象doHelloWorld排隊在事件配置設定線程上,然後列印一條消息。
Runnable doHelloWorld = new Runnable() {
public void run() {
System.out.println("Hello World on " + Thread.currentThread());
}
};
SwingUtilities.invokeLater(doHelloWorld);
System.out.println("This might well be displayed before the other message.");
如果從事件分發線程(例如,從JButton的ActionListener)調用invokeLater,則 doRun.run 仍将延遲,直到處理完所有未決事件。請注意,如果doRun.run 引發未捕獲的異常,則事件分發線程将展開(而不是目前線程)。
從1.3版本開始,此方法隻是java.awt.EventQueue.invokeLater()的封面。
與Swing的其餘部分不同,可以從任何線程調用此方法。
準則
不能在EDT中被調用,否則程式會抛出Error,請求也不會去執行。看源碼:
public static void invokeAndWait(Runnable runnable)
throws InterruptedException, InvocationTargetException {
//不能在EDT中調用invokeAndWait
if (EventQueue.isDispatchThread()) {
throw new Error("Cannot call invokeAndWait from the event dispatcher thread");
}
class AWTInvocationLock {}
Object lock = new AWTInvocationLock();
InvocationEvent event =
new InvocationEvent(Toolkit.getDefaultToolkit(), runnable, lock,
true);
synchronized (lock) {
//添加進事件隊列
Toolkit.getEventQueue().postEvent(event);
//block目前線程
lock.wait();
}
Throwable eventThrowable = event.getThrowable();
if (eventThrowable != null) {
throw new InvocationTargetException(eventThrowable);
}
}
如果invokeAndWait在EDT中調用,那麼首先将請求壓進隊列,然後EDT便被block,等待請求結束通知它繼續運作。
而實際上請求将永遠得不到執行,因為它在等待隊列的排程使EDT執行它,這就陷入一個僵局:EDT等待請求先執行,請求又等待EDT對隊列的排程。彼此等待對方釋放鎖是造成死鎖的四類條件之一。Swing有意地避免了這類情況的發生。