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());
		}

	}

}


猜你喜欢

转载自blog.csdn.net/altermanzhu/article/details/80325320
今日推荐