java swing多執行緒處理情況下UI假死的解決
阿新 • • 發佈:2019-01-22
背景&問題
專案中使用java swing做了個多執行緒處理任務的介面,在介面上顯示多執行緒任務的log資訊,為了實時顯示log資訊,使用了log4j的org.apache.log4j.WriterAppender並單獨開了執行緒。但是log資訊只在多執行緒任務結束後才一次顯示。
解決方案
SwingAPI是非執行緒安全的,也就是說不能在任意地方呼叫,它應該只在EDT中呼叫。Swing的執行緒安全靠事件佇列和EDT來保障。EventQueue的派發機制由單獨的一個執行緒管理,這個執行緒稱為事件派發執行緒(EDT)。和其他很多桌面API一樣,Swing將GUI請求放入一個事件佇列中執行。 通過EDT,使得不具備執行緒安全的Swing函式庫避開了併發訪問的問題。
因此,任何涉及多執行緒任務的UI,更新操作要放在主執行緒,將耗時的業務邏輯另起執行緒執行,保持主執行緒不被耗時任務中斷即可。
package pdf.page; ... import pdf.page.log.Log4JGUIAppenderThread; public class MakePageGUI implements ActionListener, ChangeListener { static Logger logger = Logger.getLogger(MakePageGUI.class); static final long LOG_RESTART_SIZE = 10000; JFrame frame; ... MakePage page; MakePageGUI() { page = new MakePage(); } public static void main(String[] args) { new MakePageGUI().show(); } public void show() { // 建立 JFrame 例項 frame = new JFrame("檔案分頁工具"); ... /* * 呼叫使用者定義的方法並新增元件到面板 */ placeComponents(); // 設定介面可見 frame.setVisible(true); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { page.shutdownNow(); frame.dispose(); } }); } /** * 監聽的方法 */ public void actionPerformed(ActionEvent e) { ... case "tifPage": if (null == this.input || null == this.config || null == this.output) { JOptionPane.showMessageDialog(null, "請選擇檔案檔案或目錄", "錯誤", JOptionPane.ERROR_MESSAGE); return; } beforePage(); // 耗時任務單獨起執行緒執行,不中斷UI的主執行緒 new Thread(() -> page.page(input, config, output, MakePage.PAGE_TYPE_TIF)).start(); break; case "timer": if (page.isDone()) { afterPage(); } progressBar.setValue(page.getProgress()); timePass.setText(formatTime(page.getTimePass())); break; } } // 進度條 public void stateChanged(ChangeEvent e) { if (e.getSource() == progressBar) { progressLabel.setText(page.getCompleted() + "/" + page.getVolumeSize()); } } }