[笔记]
Swing多线程编码过程中的误区
很多学JAVA程序员都是从Swing开始的,但很多人对AWT GUI线程的机制并没有太深的了解,或者说一直都只了解线程的概念,而不了解AWT对线程的使用。我发现很多人碰到线程阻塞的问题,就通过调用SwingUtilities.invokeLater()来解决。
其实这是很容易造成误会的地方:
1、 不要以为Swing 是多线程的,实际上Swing 的UI是单线程的
2、 不要以为SwingUtilities.的两个invoke是多线程,实际上它还是单线程的
3、 不要以为invokeLater的意思是当前线程执行完再执行目标线程;以为invokeAndWait的意思是等待目标线程执行完再执行当前线程,实际上压根就不是那么回事
问题代码1:大意是在按下某个按钮的时候调用一个远程服务
JButton button = new JButton();
button.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
invokeRemoteService();//可能需要等待
}
});
在swing系统中,有一个顶级的java.awt.Container(可能是一个JFrame或JDialog实例),负责启动一个EventDispatchThread线程,单线程,这个线程是负责处理UI事件的。
首先,界面Swing控件向EventDispatchThread的EventQueue提交一个event,由EventDispatchThread负责调度各个event的执行。例如,按下一个JButton的时候,JButton向EventQueue执行postEvent,提交一个ActionEvent。EventDispatchThread线程根据调度算法执行到该event的时候,会调用JButton上的processActionEvent,JButton再调用actionPerformed,这过程并没有执行任何new Thread().start()代码,也就是说JButton的ActionListener.actionPerformed()中的代码完全是在EventDispatchThread线程内执行的。
所以,假如我们在任何ActionListener、MouseListener等对象中编写耗时的逻辑,那么整个Swing系统就会出现响应迟钝的现象,更有甚者,如果在这些Listener中执行线程wait(),以等待另一个线程的锁定资源或计算结果,那么实际上就是EventDispatchThread线程被阻塞,整个系统界面就会处于无响应状态,一点反应都没有。
以上是误解1造成的,了解这个过程,就很容易看出上面这段代码的问题是什么原因了。解决的方法也倒比较简单,直接new Thread().start();就可以保证EventDispatchThread执行到当前方法的时候快速返回,以便可以去响应来自用户界面的其他事件。
问题代码2:大意是在按下某个按钮的时候调用一个远程服务,同时处理其他事情
JButton button = new JButton();
button.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
//位置A
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//位置B
invokeRemoteService();//可能需要等待
}
});
doOtherThing();
}
});
这段代码跟第一段代码唯一的差别是doOtherThing()在invokeRemoteService ()完成之前就能够得到执行,所以造成了invokeRemoteService ()/doOtherThing()好像是在两个线程里执行的假象。实际上invokeLater是把目标代码打包成一个Event提交到EventQueue去了,等到EventDispatchThread线程执行完当前代码段的doOtherThing()后,再去执行这个EventQueue中的Event,这时候就会执行到这个invokeRemoteService ()方法。但是,实际上这两个方法都是在EventDispatchThread中执行的,并没有任何其他Thread来执行。于是,问题1的问题还是没解决。实际上直接new Thread().start()方法就可以了,使用SwingUtilities完全是由于误解造成的滥用。
测试方法,在位置A和位置B都加上下面这行代码:
System.out.println(Thread.currentThread().getId() + Thread.currentThread().getName());
返回的结果都是一样的:
21AWT-EventQueue-0
21AWT-EventQueue-0
[讨论]
一般情况下(除了系统启动时后台创建的Daemon线程),系统的所有执行功能逻辑和业务逻辑的线程都应该是从界面操作触发的。我们应该清楚哪些需要或应该放到EventDispatchThread中去执行,哪些需要或应该创建一个新线程去执行,也需要清醒的知道自己当前编写的是属于什么逻辑。
这个问题我觉得应该把代码分成3层,第一层,UI层,包括UI控件上的Listener逻辑,这是应该给EventDispatchThread去执行的,必须简短高效,快速return;这一层做不完的事情通过new Thread().start()交给下一层去做,我称之为控制层;然后控制层再去调用具体的业务代码,即第三层,业务层。所有由UI控件触发的逻辑都应该这么分。
另一个问题是,Swing并不推荐在EventDispatchThread之外修改界面,那么,如果我们在业务层需要repaint某个控件,或者updateUI应该怎么办呢,那就可以使用SwingUtilities来处理了,这才是正确使用SwingUtilities的场景,也是设计这个工具的目的。
分享到:
相关推荐
Swing 线程之 SwingUtilities invokeLater Swing 线程模型是 Java 中一个非常重要的概念,它是 Java 的图形用户界面(GUI)程序设计中的一部分。Swing 是基于 MVC 模式(Model-View-Controller)设计的,它提供了一...
在多线程环境中,多个线程同时访问共享资源可能导致数据不一致或错误。为了解决这个问题,Swing引入了单线程模型,即EDT。EDT是一个专用于处理用户界面事件的线程,包括按钮点击、窗口关闭等。所有的Swing组件修改和...
在Java开发中,Swing库是用来构建图形用户界面(GUI)的工具包,而多线程则是提升程序并发性能和响应能力的关键技术。Swing虽然是Java语言的一部分,但它设计为单线程模型,主要是为了简化GUI编程并避免复杂的同步...
Java Swing 是一个用于构建桌面应用程序的用户界面工具包,它是Java Foundation Classes (JFC) 的一部分...对于初学者来说,通过研究提供的 "几个swing多线程的例子",可以更好地理解如何在实践中结合 Swing 和多线程。
在Java Swing中,我们可以利用组件、布局管理器、事件处理等特性来创建丰富的用户界面。多线程是计算机程序设计中的关键概念,尤其在GUI编程中,它允许我们同时执行多个任务,提升程序的响应性和效率。 在这个...
一个建议是那些在Swing中使用的类进行合理的设计。代码执行前判断是否处于Swing线程当中(使用SwingUtilities.isEventDispatchThread()方法),如果不是,则需要通过SwingUtilities.invokeLater(Runnable)执行,否则...
### Java开发中的线程安全选择与Swing 在Java开发中,Swing作为构建桌面应用程序图形用户界面(GUI)的主要工具之一,其设计...这些技术的应用可以有效地避免多线程环境下的问题,并确保应用程序的稳定性和可靠性。
在Swing中,线程管理是非常关键的概念,因为它涉及到用户界面的响应性和程序的正确性。以下是关于Swing中线程使用的一些核心知识点: 1. **线程模式**: - **主线程**:每个Swing应用始于一个主线程,即`main`方法...
Swing中的`JFrame`是窗口的基础组件,通常作为应用程序的主要容器。为了在`JFrame`上显示时间,我们需要一个`JLabel`或`JTextField`这样的文本组件,它们可以用来展示文本信息,如当前时间。 ```java import javax....
在这个程序中,Java的Swing库被用来创建窗口面板,而多线程技术则用于实现发牌过程的并发执行,确保程序的响应性和流畅性。 首先,让我们深入了解Java的Swing库。Swing是Java的标准GUI工具包,提供了丰富的组件,如...
这个过程涉及到了多线程的概念,以确保界面的流畅性和数据处理的并发性。 首先,Swing组件本身是基于事件驱动模型的,这意味着用户的每一个操作(如按钮点击)都会触发一个事件,然后由事件处理器(event listener...
Swing程序中的线程主要包括三种类型:初始化线程、UI事件调度线程(Event Dispatch Thread,简称EDT)以及任务线程。 1. 初始化线程: 每个Java程序都从main方法开始,它运行在初始化线程上。这个线程负责读取程序...
Swing线程基础是Java GUI编程中的重要概念,特别是对于使用Swing库构建桌面应用程序的开发者而言。Swing设计遵循单线程模型,确保UI组件的线程安全性和响应性。以下是Swing线程基础的详细说明: 1. **Swing应用程序...
本篇文章将深入探讨如何使用Java Swing实现一个单任务多线程的下载程序。 首先,我们需要理解多线程的概念。在Java中,线程是程序执行的最小单元,每个线程负责一部分任务。多线程允许同时执行多个任务,提高程序的...
`SwingUtilities.invokeLater()`通常用于将任务插入到事件队列中,以便在下一个事件循环中由Swing线程执行。而`SwingWorker`则提供了更高级的功能,可以在后台线程执行计算,并在完成后更新UI。`SwingWorker`允许你...
1. **事件调度器线程(Event Dispatch Thread, EDT)**:Swing中的所有用户界面更新都必须在EDT中进行,以保证界面的同步和一致性。因此,当后台线程完成任务后,需要通过`SwingUtilities.invokeLater()`或`...
在多线程环境中,该类需要确保更新UI的操作是线程安全的,可能通过`SwingUtilities.invokeLater()`或`javax.swing.SwingWorker`来实现。 `testframe.java`可能是整个应用程序的主入口点,它创建并启动线程,使得...
任务线程与Swing组件的交互必须在EDT中进行,通常通过`SwingUtilities.invokeLater`或`invokeAndWait`方法来实现。 Swing编程中应注意以下几点: - **禁止直接从其他线程访问UI组件**:这样做可能导致界面更新和...
同时,为了更新用户界面,可能使用了Swing的异步事件调度模型,如SwingUtilities.invokeLater()或SwingWorker,确保在事件调度线程中安全地修改UI组件。 总结起来,"Java Swing 赛马小游戏 线程实例"涵盖了以下关键...