在Swing應用程式中,按鈕事件處理器若直接呼叫Thread.sleep(3000)或執行耗時任務,會阻塞事件派發執行緒(EDT),導致setText()、repaint()、validate()皆無法即時更新畫面,使用者看不到「正在清空...」提示,直接卡住3秒後跳到結果。本文說明正確的非同步處理模式。
outPrint.setText("其他地方數據正在清空...");
outPrint.repaint(); // 無效,EDT被佔用無法重繪
outPrint.validate(); // 無效
try {
Thread.sleep(3000); // 災難:阻塞EDT,整個UI凍結
} catch (InterruptedException e1) {
e1.printStackTrace();
}
outPrint.setText("其他地方數據正在清空...");
// 方案一:匿名Thread(簡單案例適用)
new Thread(() -> {
try {
// 執行耗時操作:清空資料、呼叫Web Service、大量運算等
Thread.sleep(3000); // 模擬耗時
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 必須回到EDT更新UI
SwingUtilities.invokeLater(() ->
outPrint.setText("清空完畢")
);
}
}).start();
new SwingWorker<Void, Integer>() {
@Override
protected Void doInBackground() throws Exception {
for (int i = 0; i <= 100; i += 10) {
Thread.sleep(300); // 模擬分階段任務
publish(i); // 發布進度
if (isCancelled()) break;
}
return null;
}
@Override
protected void process(List<Integer> chunks) {
// 在EDT中接收進度更新UI
outPrint.setText("清空進度: " + chunks.get(chunks.size()-1) + "%");
}
@Override
protected void done() {
try {
get(); // 拋出doInBackground中的例外
outPrint.setText("清空完畢");
} catch (Exception e) {
outPrint.setText("失敗: " + e.getMessage());
}
}
}.execute();
SwingUtilities.invokeLater()、SwingWorker.publish/process、或EventQueue.invokeLater()回到EDT。repaint()、revalidate()只是排程重繪,若EDT被阻塞,重繪永遠不會執行。掌握SwingWorker模式,即可優雅解決所有「長按鈕操作導致UI凍結」的問題。