Swing线程机制
首先swing是单线程的,这个是这篇文章的前提,也是意义所在,当swing界面程序启动的时候,会启动3个进程,
1、主线程
2、系统工具包线程:负责捕获操作系统事件,然后将事件转换成swing的事件,然后发送到事件派发线程EDT
3、事件派发线程(EDT):将事件派发到各个组件,并负责调用绘制方法更新界面
所有的事件,例如键盘,鼠标事件,都会由工具包线程转换成swing事件,然后放到事件队列EventQueue中,而这个EventQueue的派发机制是由EDT来管理的。
所以任何修改组件状态的方法都应该在EDT中执行,包括构造方法。Swing这样的构造原理经常会造成的情况就是,在EDT中执行长时间的事件,使EDT不能及时响应更新界面的事件,就是所说的界面卡住,这种不光是新手就是比较熟练的程序员也会犯的一个错误。所以必须避免在EDT中执行长时间的操作,而避免的方法就是多线程,启动另外的线程来处理冗长的操作,比如操作数据库,读写文件等,在这过程中可能要更新界面来给用户以提示,比如显示一个进度条,过一段事件更新一下界面,但是在EDT以外的线程中更新界面都是无效的,这在前面已经说过,要更新界面就要将对界面的更新操作放到EDT中,但是事件又是在另外的线程中执行的,要解决这个问题就要使用SwingUtilities提供的一个方法了 invokeLater,
Java代码
1. public void actionPerformed(ActionEvent e){
2. new Thread(new Runnable(){
3. //do something
4. SwingUtilities.invokeLater(new Runnable(){
5. pulic void run(){
6. //update the GUI
7. }
8. });
9. }).start;
10. }
这个方法的作用就是将一个更新界面的任务放到EDT中,EDT会在适当的时候进行调用以更新界面。invokeLater负责创建一个含有Runnable的特定事件,并让其在EDT中排队等待调用,当被调用时就会运行Runnable中的run方法进行派发。
invokeLater和invokeAndWait的区别
与invoikeLater一样,invokeAndWait也把可运行对象排入事件派发线程的队列中,invokeLater在把可运行的对象放入队列后就返回,而invokeAndWait一直等待知道已启动了可运行的run方法才返回。如果一个操作在另外一个操作执行之前必须从一个组件获得信息,则invokeAndWait方法是很有用的。
一般我们用到invokeLater,都是为了执行事件派发线程中的代码,将一段更新UI事件的代码派发到EventQueue中,但是我们可以从事件派发线程中调用invokeLater,却不能从事件派发线程中调用invokeAndWait,下面我们用代码来例证它们的不同:
SwingConsole.java文件:
Java代码
1. package swing.utilities;
2. import javax.swing.*;
3. public class SwingConsole {
4. public static void run(final JFrame f, final int width, final int height){
5. SwingUtilities.invokeLater(new Runnable(){
6. public void run(){
7. f.setTitle(f.getClass().getSimpleName());
8. f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
9. f.setSize(width, height);
10. f.setVisible(true);
11. }
12. });
13. }
14. }
TestAction.java文件:
Java代码
1. import java.awt.BorderLayout;
2. import java.awt.FlowLayout;
3. import java.awt.event.ActionEvent;
4. import java.awt.event.ActionListener;
5. import javax.swing.*;
6. import swing.utilities.SwingConsole;
7.
8. public class TestAction extends JFrame {
9. private static final long serialVersionUID = -7462155330900531124L;
10. private JButton jb1 = new JButton("确定");
11. private JTextField txt = new JTextField(10);
12.
13. public TestAction() {
14. jb1.addActionListener(new ActionListener() {
15. public void actionPerformed(ActionEvent e) {
16. String name = ((JButton) e.getSource()).getText();
17. txt.setText(name);
18. }
19. });
20. setLayout(null);
21. add(txt);
22. add(jb1);
23. txt.setBounds(50, 100, 200, 30);
24. jb1.setBounds(270, 100, 70, 30);
25. }
26.
27. public static void main(String[] args) {
28. SwingUtilities.invokeLater(new Runnable() {
29. public void run() {
30. SwingConsole.run(new TestAction(), 500, 500);
31. }
32. });
33.
34. }
35.
36. }
我们在启动main线程的时候就把整个SwingConsole派发到EventQueue中,而本身SwingConsole已经处在EventQueue中,我们调用invokeLater 没问题,运行正常!
再看我们修改之后的:
SwingConsole.java文件:
Java代码
1. package swing.utilities;
2. import java.lang.reflect.InvocationTargetException;
3.
4. import javax.swing.*;
5. public class SwingConsole {
6. public static void run(final JFrame f, final int width, final int height){
7. try {
8. SwingUtilities.invokeAndWait(new Runnable(){
9. public void run(){
10. f.setTitle(f.getClass().getSimpleName());
11. f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
12. f.setSize(width, height);
13. f.setVisible(true);
14. }
15. });
16. } catch (InterruptedException e) {
17. throw new RuntimeException(e);
18. } catch (InvocationTargetException e) {
19. throw new RuntimeException(e);
20. }
21. }
22. }
TestAction.java文件:
与前面一样
运行结果,出现异常:Exception in thread "AWT-EventQueue-0" java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread
在这里,我们把SwingConsole中的invokeLater改成了invokeAndWait,而之前SwingConsole已经处在EventQueue中了。
所以我们可以总结出它们的不同:可以从事件派发线程中调用invokeLater,却不能从事件派发线程中调用invokeAndWait,从事件派发线程调用invokeAndWait的问题是:invokeAndWait锁定调用它的线程,直到可运行对象从事件派发线程中派发出去并且该可运行的对象的run方法激活,如果从事件派发线程调用invoikeAndWait,则会发生死锁的状况,因为invokeAndWait正在等待事件派发,但是,由于是从事件派发线程中调用invokeAndWait,所以直到invokeAndWait返回后事件才能派发。
在这里我们还可以看到,在main函数里,我们在一开始就把整个操作都置于EventQueue中,这样虽然安全,但是不适合新手,因为新手很容易犯刚才那样的错误,一不小心就用了invokeAndWait而产生死锁。对于swing中的线程,我也还没有找到理想的处理机制,所以暂时就先到此,以后有经验才跟大家分享吧。
分享到:
相关推荐
2. **Swing线程中的EDT运行机制** - EDT从事件队列中取出事件,然后调用相应组件的`dispatchEvent`方法进行处理。 - `processEvent`方法会调用注册在组件上的事件处理器函数。 - 事件处理器函数根据事件执行相应...
序 前言 第一部分 Swing基础 第1章 简介 1.1 Swing的历史 1.2 轻量组件与重量组件的比较 1.3 Swing组件 1.3.1 AWT的替代组件... 2.4.2 SwingUtilties类的invokeLater和invokeAndWait方法 2.5 本章回顾
Swing线程机制是Swing库中的关键概念,它确保了所有对Swing组件的操作都在正确的线程——事件分发线程(Event Dispatch Thread,简称EDT)上执行,以避免线程安全问题和界面更新不一致的情况。`SwingUtilities....
Swing线程基础是Java GUI编程中的重要概念,特别是对于使用Swing库构建的应用程序而言。Swing设计遵循单一线程原则,确保图形用户界面(GUI)的稳定性和响应性。以下是Swing线程基础的详细说明: 1. **初始化线程 ...
总的来说,理解和正确使用Swing线程机制是构建高效、响应迅速的Java GUI应用程序的基础。遵循线程规则,确保UI操作在EDT中进行,而耗时任务在单独的线程中执行,可以避免界面卡死,提升用户体验。
chm格式的,内容很全面。就是一本书。 目 录 序 前言 第一部分 Swing基础 ... 2.4.2 SwingUtilties类的invokeLater和invokeAndWait方法 2.5 本章回顾 第3章 Swing组件的体系结构 .......
为了确保Swing组件的操作是在EDT中执行,Swing提供了`SwingUtilities.invokeLater(Runnable)`和`SwingUtilities.invokeAndWait(Runnable)`两个方法来帮助开发者解决线程安全问题。 - **SwingUtilities.invokeLater...
3. **Runnable 和 Thread**: 如果任务相对简单,也可以直接使用 `Runnable` 和 `Thread` 创建新线程,但在 Swing 中,仍需确保将更新 UI 的代码放入 `SwingUtilities.invokeLater()` 或 `invokeAndWait()` 方法中,...
### Swing速度慢与响应迟钝的原因及解决方案 #### 原因分析 ...综上所述,通过正确理解和运用Swing的事件处理机制、合理利用多线程技术和不断优化数据处理逻辑,可以显著提升Swing应用程序的速度和用户体验。
Swing本身也提供了一些与线程交互的工具,如SwingUtilities的invokeLater()和invokeAndWait()方法,它们用于在EDT上安全地执行代码。例如,当飞机击中目标,分数更新的操作就需要通过invokeLater()方法添加到EDT的...
- 如何利用Swing Utilities的invokeLater和invokeAndWait方法确保在正确线程上更新UI。 - 自定义组件的高级技巧,如绘图、动画和动态效果的实现。 - 使用SwingWorker来执行耗时任务,避免阻塞用户界面。 通过深入...
如果需要在非EDT线程中修改Swing组件,必须使用SwingUtilities的invokeLater或invokeAndWait方法。 SwingWorker是JDK 1.6引入的一个类,用于处理耗时任务。在doInBackground方法中执行计算密集型任务,然后通过...
在Swing应用中,我们还可以利用 Swing Utilities 工具类的一些方法,如 invokeLater 和 invokeAndWait,来确保在正确的线程(事件调度线程)中执行 GUI 相关的操作,避免出现线程安全问题。 总之,Swing 是 Java ...
Swing是Java编程语言中用于构建图形用户界面(GUI)的一个工具包,它是Java Foundation Classes (JFC)的一部分。...Swing的组件库、事件处理机制、布局管理和线程安全特性共同构建了这个简单的GUI应用。
6. **SwingUtilities**: 这是Swing提供的一些实用工具类,如invokeLater()和invokeAndWait()方法,它们用于在事件调度线程中执行代码,确保GUI更新的正确性和线程安全性。 7. **SwingWorker**: 为了解决GUI线程阻塞...
Swing线程在同一时刻仅能被一个线程所访问。一般来说,这个线程是事件派发线程(event-dispatching thread)。 如果需要从事件处理(event-handling)或绘制代码以外的地方访问UI,那么可以使用SwingUtilities类的...
为了确保界面的响应性和避免竞态条件,开发者需要使用SwingUtilities的invokeLater或invokeAndWait方法。 8. **Swing的高级特性**: Swing还提供了对话框(JOptionPane)、拖放支持、国际化和本地化、以及对...
Swing是单线程模型,这意味着所有的UI更新都必须在称为“事件调度线程”(Event Dispatch Thread, EDT)的线程中执行。这是为了确保界面始终是响应式的,避免因为长时间的计算任务阻塞用户界面。`EventQueue`就是这...
- **线程操作**:对Swing组件的线程操作需要特别注意,避免在非EDT中直接修改组件状态,应使用`SwingUtilities.invokeLater()`或`invokeAndWait()`来确保操作在EDT中执行。 - **顶级容器**:建议在Swing应用中使用...