简析Java Swing线程模型
2014-03-23 20:03
495 查看
Java Swing的线程模型是一个基于事件队列的单线程模型。任何时候运行一个Swing应用程序,都会自动创建3个线程。第一个是应用程序的主线程,它运行应用程序的主方法,主方法一旦运行结束,该线程就消亡了。第二个是工具包线程,它负责捕获系统产生的各种事件。这个线程是AWT的实现的一部分,它也是相当重要的,但是它从来都不会去执行应用的代码,只把捕获到的事件发送给第三个线程,因此编写Swing应用一般不用去考虑该线程。最后一个就是事件分配线程(EDT),这个线程对于编写Swing应用的程序员来说相当重要。它负责把工具包线程捕获到的事件分派给适当的组件,并调用它们相应的绘制方法。同时与Swing进行的交互也是通过EDT,例如在一个JButton上面点击鼠标,就会产生ActionEvent,而该事件则由EDT分派给对应的监听器,并执行对应监听器的对应方法。如果一个组件需要重绘来更新自己的显示,它会产生一个重绘请求并把请求加入到事件队列中。当EDT从事件队列中获取到该重绘请求时,它就会调用对应组件的重绘方法来重新绘制该组件。因此,Swing中的事件的处理和组件的更新都依赖于EDT。
Swing的单线程模型并不复杂。但如果编写Swing应用的程序员并不了解该模型,就很可能编写出效率低下、响应缓慢,甚至卡死的图形界面程序。EDT几乎负责了与Swing有关的所有事情,因此如果在EDT上面执行长时间的计算操作或者长时间的I/0操作,就很可能会导致Swing响应缓慢,甚至于卡死。例如,一个刚接触Swing的人很可能会写出这样的代码:
该程序当点击按钮之后,EDT就会被长时间的I/O操作给阻塞了。于是事件的分派跟界面的更新都被阻塞了。在用户看来,这样的程序响应很慢,甚至于就像中止了一样。有些人不了解Swing的线程机制,觉得Swing的效率很低。但事实上Swing组件非常快速地分派它们的工作。只有当应用程序有某项任务阻塞了EDT才会导致Swing的响应缓慢。因此,编写Swing应用的时候,不要在EDT上执行长时间的计算任务或者I/O操作,在EDT上执行的任务尽可能短小快速,避免阻塞EDT。当需要执行一项长时间的任务时,应该新建一个工作线程来处理。比如上面提到的那段代码,可以改成这样:
还有Swing不是一个线程安全的API,它遵循一个单一的规则:EDT负责执行所有改变组件状态的方法,包括组件的构造方法。由于Swing不是线程安全的,所以不在EDT上面更新组件的状态,可能会破坏组件的数据和状态。如果当某个工作线程完成了一项长时间的任务,然后需要更新组件的状态的时候,我们可以使用EventQueue.invokeLater方法来向EDT发送一项新的任务。该任务会进入事件队列等待EDT的执行。invokeLater的用法如下:
总的来说,Swing的线程模型是单线程的。EDT是其核心。编写Swing应用时,不要在EDT上执行耗时很长的任务,耗时很长的任务应该通过工作线程来完成。同时Swing API不是线程安全的,要遵守单一的规则,即EDT负责完成所有改变组件状态的方法。
Swing的单线程模型并不复杂。但如果编写Swing应用的程序员并不了解该模型,就很可能编写出效率低下、响应缓慢,甚至卡死的图形界面程序。EDT几乎负责了与Swing有关的所有事情,因此如果在EDT上面执行长时间的计算操作或者长时间的I/0操作,就很可能会导致Swing响应缓慢,甚至于卡死。例如,一个刚接触Swing的人很可能会写出这样的代码:
public class Bad extends JFrame{ private JButton button; public Bad(){ button = new JButton("读取"); button.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { //进行长时间I/0操作,比如读取一个大文件。 } }); add(button,BorderLayout.SOUTH); setSize(800,600); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args){ Bad bad = new Bad(); } }
该程序当点击按钮之后,EDT就会被长时间的I/O操作给阻塞了。于是事件的分派跟界面的更新都被阻塞了。在用户看来,这样的程序响应很慢,甚至于就像中止了一样。有些人不了解Swing的线程机制,觉得Swing的效率很低。但事实上Swing组件非常快速地分派它们的工作。只有当应用程序有某项任务阻塞了EDT才会导致Swing的响应缓慢。因此,编写Swing应用的时候,不要在EDT上执行长时间的计算任务或者I/O操作,在EDT上执行的任务尽可能短小快速,避免阻塞EDT。当需要执行一项长时间的任务时,应该新建一个工作线程来处理。比如上面提到的那段代码,可以改成这样:
public class Good extends JFrame{ private JButton button; public Good(){ button = new JButton("读取"); button.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { //新建一个工作线程来处理长时间任务 Thread t = new Thread(new Runnable() { @Override public void run() { //进行长时间I/0操作,比如读取一个大文件。 } }); t.start(); } }); add(button,BorderLayout.SOUTH); setSize(800,600); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args){ Good bad = new Good(); } }
还有Swing不是一个线程安全的API,它遵循一个单一的规则:EDT负责执行所有改变组件状态的方法,包括组件的构造方法。由于Swing不是线程安全的,所以不在EDT上面更新组件的状态,可能会破坏组件的数据和状态。如果当某个工作线程完成了一项长时间的任务,然后需要更新组件的状态的时候,我们可以使用EventQueue.invokeLater方法来向EDT发送一项新的任务。该任务会进入事件队列等待EDT的执行。invokeLater的用法如下:
EventQueue.invokeLater(new Runnable(){ @Override public void run(){ //要在EDT上执行的任务 } });
总的来说,Swing的线程模型是单线程的。EDT是其核心。编写Swing应用时,不要在EDT上执行耗时很长的任务,耗时很长的任务应该通过工作线程来完成。同时Swing API不是线程安全的,要遵守单一的规则,即EDT负责完成所有改变组件状态的方法。
相关文章推荐
- Java线程:线程栈模型与线程的变量
- Java线程----生产者消费者模型
- java线程间通信[实现不同线程之间的消息传递(通信),生产者和消费者模型]
- java线程内存模型,线程、工作内存、主内存
- 深度剖析java线程安全|内存模型|生产消费者模式|
- Java学习札记之线程模型——生产消费模型
- 【Java基础】线程笔记——内存模型
- Java线程:并发协作-生产者消费者模型
- Java 线程/内存模型的缺陷
- Java线程模型缺陷研究
- Java线程:线程栈模型与线程的变量
- java多线程-线程内存模型
- Java 线程通信内存模型---主内存与工作内存
- java线程深度解析(五)——并发模型(生产者-消费者)
- Java线程:并发协作-生产者消费者模型
- java线程-从生产者和消费者模型说起
- 08 JAVA 线程 内存模型(一)
- Java、C#线程模型分析对比
- java界面编程(4) ------ Swing事件模型
- Java线程:并发协作-生产者消费者模型