1. 程式人生 > >java swing多執行緒處理情況下UI假死的解決

java swing多執行緒處理情況下UI假死的解決

背景&問題

專案中使用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());
		}

	}

}