所有的事件处理都在Event Dispatch Thread(EDT)上进行,此一类事件模型通常叫做单线程模型。
这种模型规定所有对组件的访问操作必须在EDT 上完成。
为什么对于组件的访问需要在EDT 上完成?这主要是为了保证对于组件状态的改变是同步的,保证了界面组件的可确定性。这种模型是大部分图形用户界面工具采用的模型,包括Swing/AWT、SWT、GTK、WinForm 等等.
?
这种模型的好处是,结构设计和代码实现都比较简单,避免了为了实现线程同步的复杂处理。
但是也带来了一些问题,最常见的问题是,程序员容易将长时间复杂任务的处理放在事件处理函数完成,造成EDT 线程被阻塞,给用户造成界面失去响应的错觉。
?
其实人们对于Swing 速度慢和反映迟钝的感觉大部分来源于此,简单的说,是程序员的问题,而不是Swing 自身的问题,是因为程序员没有理解这种事件处理机制造成的。
其实在SWT、GTK、WinForm 等任何以这种事件模型为基础的工具都会出现。
重现的方法就是你简单的将长时间处理的任务放在事件处理函数中,你的用户界面就会失去响应。
如何解决这种问题?
通用的办法就是采用异步线程处理长时间任务。
但是还要记住的是,在这种任务中对于界面的更新要采用SwingUtilities.invokeLater 或者在SWT 采用Synchronize 方法,将访问操作放到EDT 上进行。
?
?示例
?
?示例代码
class="java">package test; import java.awt.BorderLayout; import java.awt.EventQueue; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import javax.swing.JButton; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; public class Frame1 extends JFrame { private static final long serialVersionUID = 1L; private JPanel contentPane; /** * Launch the application. */ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { Frame1 frame = new Frame1(); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } /** * Create the frame. */ public Frame1() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 450, 300); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); contentPane.setLayout(new BorderLayout(0, 0)); setContentPane(contentPane); final JLabel label = new JLabel(); contentPane.add(label, BorderLayout.CENTER); JButton btnNewButton = new JButton("按钮"); contentPane.add(btnNewButton, BorderLayout.SOUTH); btnNewButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { label.setText("开始执行..."); //耗时操作,新启线程去处理,但是外观的改变,仍需要放到EDT去执行。 Thread thread = new Thread() { @Override public void run() { try { //更新外观 SwingUtilities.invokeLater(new Runnable(){ public void run() { label.setText("正在执行耗时任务..."); } }); // 模拟耗时操作 Thread.sleep(5*1000); //更新外观 SwingUtilities.invokeLater(new Runnable(){ public void run() { label.setText("耗时任务执行完毕"); } }); } catch (Exception e2) { e2.printStackTrace(); } } }; thread.start(); } }); } }
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?