Swing 設計目標為強大、靈活、易用,且便於開發者擴充元件。為達成此目標,Swing 不要求元件支援多執行緒存取,而是採用單一執行緒規則:所有可能影響已實現(realized)元件狀態的代碼,必須在事件分派執行緒(Event Dispatch Thread, EDT)中執行。這消除了複雜的同步化問題,簡化元件開發。
paint() 方法已被或可能被呼叫。頂層視窗呼叫 setVisible(true)、show()、pack() 時實現;加入已實現容器也會實現。paint())與事件處理(actionPerformed())的專屬執行緒。亦可透過 SwingUtilities.invokeLater() 將代碼排入 EDT 執行。一旦 Swing 元件被實現,所有影響或依賴其狀態的代碼必須在 EDT 執行。
This method is thread safe, although most Swing methods are not. 的方法。JComponent 的 repaint()、revalidate()、invalidate() 可從任何執行緒安全呼叫。pack() 實現元件後,show() 應在 EDT 執行,但緊接著無其他 GUI 代碼實際上安全。init() 中建構 GUI 安全(瀏覽器不會在 init()/start() 前繪製),但不得在其中呼叫 show()/setVisible(true)。// 非阻塞,排入 EDT 隊列尾端
SwingUtilities.invokeLater(() -> {
label.setText("Updated from background thread");
});
// 阻塞,等待 EDT 執行完畢
SwingUtilities.invokeAndWait(() -> {
// 需立即看到結果的操作
});
用於耗時計算或 I/O 操作,避免阻塞 EDT 導致介面凍結。
SwingWorker<Void, Integer> worker = new SwingWorker<>() {
@Override
protected Void doInBackground() throws Exception {
for (int i = 0; i <= 100; i++) {
Thread.sleep(50); // 模擬耗時任務
publish(i); // 發送進度到 EDT
}
return null;
}
@Override
protected void process(List<Integer> chunks) {
progressBar.setValue(chunks.get(chunks.size() - 1)); // EDT 更新 UI
}
@Override
protected void done() {
JOptionPane.showMessageDialog(null, "完成!");
}
};
worker.execute();
Timer timer = new Timer(1000, e -> {
label.setText("Time: " + new Date()); // 在 EDT 執行
});
timer.start();
掌握「單一執行緒規則」、善用 invokeLater、SwingWorker、Timer,即能編寫線程安全、回應迅速的 Swing 應用程式。