前言
因为很多人会见到一些源代码中调用SwingUtilities的invokeLater或者invokeAnd-Wait方法,但是却不理解它们到底起到了什么作用,本文的目标就是让你理解这两个方法的意义。本文是swing编程基础且不可越过的一节。而且,如你所见,本文的副标题是“Swing程序的线程处理”,其实这是对本文内容更好的概括。
事件派发线程(EDT)
理解SwingUtilities类作用的前提是先理解事件派发线程的概念。
当运行一个 Swing 程序时,会自动创建三个线程。
1.主线程,负责执行main 方法。
2. toolkit 线程,负责捕捉系统事件,比如键盘、鼠标移动等,程序员不会有任何代码在这个线程上执行。Toolkit线程的作用是把自己捕获的事件传递给第三个线程,也就是事件派发线程。
3. 事件派发线程(EDT,Event Dispatcher Thread),顾名思义是用来派发事件(根据事件找到对应的事件处理代码)的线程。EDT接收来自 toolkit 线程的事件,并且将这些事件组织成一个队列,EDT的工作内容就是将这个队列中的事件按照顺序派发给相应的事件监听器,并且调用事件监听器中的回调函数,这也意味着,所有的事件处理代码都是在EDT而不是主线程中执行。
上面说到EDT中维护了一个事件的队列,并且它们是按照顺序派发的。由于事件派发是单线程的操作,所以只有等待前面事件监听器的回调函数执行完毕,才能够执行组件更新的操作,以及继续派发后面的事件。这样导致的一个后果就是:当在一个事件监听回调函数中做了耗时的操作,那么,界面会因此停住,并且界面上所有控件失效(不可触发)。
解决这个问题的方法是:在事件处理函数中将耗时的操作放到新线程(一般称之为工作线程)中执行,而不是让其在EDT中执行。比如下面的例子。
案例
一个窗口,有一个按钮和一个label。点击按钮,系统将做模仿导入数据的动作,导入数据之前需要检测数据的合法性。并且,检测数据和导入数据这两个步骤都需要耗费一定的时间。
如果没有之前说到的EDT的概念,那么你可能会这么做:
- importBtn.addActionListener(newActionListener() {
- @Override
- publicvoid actionPerformed(ActionEvent e) {
- try{
- lb.setText("1.检查数据合法性...");
- Thread.sleep(3000);//模仿检测数据合法性
- lb.setText("2.正在导入数据...");
- Thread.sleep(4000);//模仿导入数据
- lb.setText("3.导入成功!");
- }catch (InterruptedException e1) {
- e1.printStackTrace();
- }
- }
- });
但是,如果运行一下的话,会发现现象是这样:点击按钮,界面卡住,按钮变得不可触发,直到一段时间(7秒)之后界面显示“3.导入成功”。期间并没有显示“1.检查数据合法性”和“2.正在导入数据”。
这个现象印证了上面说的理论:当事件派发线程中正在执行的事件监听函数执行完毕,才能进行UI组件的刷新操作,并且派发事件队列中的下一个。
下面是修改后的代码,将耗时的操作放在一个新的工作线程中执行:
- importBtn.addActionListener(newActionListener() {
- @Override
- public voidactionPerformed(ActionEvent e) {
- new Thread(new Runnable() {//开辟一个工作线程
- @Override
- public void run() {
- try {
- lb.setText("1.检查数据合法性...");
- Thread.sleep(3000);//模仿检测数据合法性
- lb.setText("2.正在导入数据...");
- Thread.sleep(4000);//模仿导入数据
- lb.setText("3.导入成功!");
- } catch(InterruptedException e1) {
- e1.printStackTrace();
- }
- }
- }).start();
- }
- });
执行结果很令人满意:点击按钮,显示检查数据合法性,三秒钟之后显示正在导入数据,四秒钟之后显示导入完毕。
主题
下面到了SwingUtilities的内容。在swing编程中有一个编程原则:所有的界面相关的更新,都应该在 EDT 上执行,否则会导致界面绘制出现不稳定性错误。这也就意味着上面代码的lb.setText("2.正在导入数据...");应该在EDT中,而非新的工作线程中执行。这样就出现了一个矛盾:耗时的操作必须要在工作线程中执行,否则会出现界面刷新不及时和卡顿的现象,而工作线程中的界面刷新代码又会导致界面绘制的不稳定。
SwingUtilities可以解决这个矛盾。SwingUtilities的invokeLater和invokeAndWait方法可以将一个可执行对象(Runnable)实例追加到EDT的可执行队列中。那么最后的代码应该是这样的:
- importBtn.addActionListener(newActionListener() {
- @Override
- public voidactionPerformed(ActionEvent e) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public voidrun() {
- lb.setText("1.检查数据合法性...");
- }
- });
- Thread.sleep(3000);//模仿检测数据合法性
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public voidrun() {
- lb.setText("2.正在导入数据...");
- }
- });
- Thread.sleep(4000);//模仿导入数据
- SwingUtilities.invokeLater(newRunnable() {
- @Override
- public voidrun() {
- lb.setText("3.导入成功!");
- }
- });
- } catch(InterruptedException e1) {
- e1.printStackTrace();
- }
- }
- }).start();
- }
- });
关于invokeLater和invokeAndWait的区别,请自行搜索。
结论
通过上面内容可以总结以下两个swing编程原则
所有的界面相关的更新,都应该在 EDT 上执行
而耗时的后台运行,不应该在 EDT 上执行
转自:http://blog.csdn.net/yanwushu/article/details/39434159
相关推荐
除了`invokeLater`,Swing还提供了`invokeAndWait`方法,它会阻塞当前线程直到指定的任务完成。但是,通常推荐使用`invokeLater`,因为它非阻塞性质允许应用程序保持响应性,特别是在处理耗时操作时。 总的来说,...
为了确保Swing组件的操作是在EDT中执行,Swing提供了`SwingUtilities.invokeLater(Runnable)`和`SwingUtilities.invokeAndWait(Runnable)`两个方法来帮助开发者解决线程安全问题。 - **SwingUtilities.invokeLater...
如果需要在非EDT线程中修改Swing组件,必须使用SwingUtilities的invokeLater或invokeAndWait方法。 SwingWorker是JDK 1.6引入的一个类,用于处理耗时任务。在doInBackground方法中执行计算密集型任务,然后通过...
SwingUtilities.invokeLater和SwingUtilities.invokeAndWait等方法提供了一种安全的机制,可以在非EDT线程中安全地执行Swing组件的操作。 ### Lambda表达式与回调接口 Lambda表达式是一种允许将代码块作为方法参数...
因此,开发者需要了解和使用SwingUtilities的invokeLater和invokeAndWait方法。 通过"ch9"、"ch7"、"ch6"、"ch8"这些章节,我们可以依次深入学习Swing的高级特性、事件处理机制、组件的使用以及布局管理等内容,...
在这个例子中,`SwingUtilities.invokeLater()`被用来在EDT中创建和显示一个`JFrame`。如果不在EDT中执行这些操作,可能会导致界面无法正确显示或者出现其他未定义的行为。 总的来说,理解和正确使用`EventQueue`...
因此,通常我们会使用SwingUtilities的invokeLater或invokeAndWait方法来确保所有对GUI的操作都在EDT中执行。 最后,第9章的第二部分可能涵盖了更高级的话题,比如对话框(JOptionPane)、拖放功能(Drag and Drop...
因此,涉及UI更新的操作必须在EDT中执行,可以通过SwingUtilities的invokeLater或invokeAndWait方法实现。 10. **可扩展性**:一个完善的记事本程序可能还包含其他功能,如查找替换、撤销重做、复制粘贴等。这些...
因此,你需要使用SwingUtilities的invokeLater或invokeAndWait方法来确保UI操作的正确性。 7. **国际化和本地化**:Swing支持多语言环境,开发者可以为不同的地区提供相应的资源文件,实现应用程序的国际化和本地化...
任务线程与Swing组件的交互必须在EDT中进行,通常通过`SwingUtilities.invokeLater`或`invokeAndWait`方法来实现。 Swing编程中应注意以下几点: - **禁止直接从其他线程访问UI组件**:这样做可能导致界面更新和...
可以使用SwingUtilities的invokeLater和invokeAndWait方法来保证线程安全。 10. **国际化和本地化**:Java GUI支持多语言环境,你可以为不同的区域和语言创建资源文件,使程序能够适应不同的文化背景。 通过学习...
使用SwingUtilities的invokeLater或invokeAndWait方法可以确保操作在EDT上执行。 SwingWorker: SwingWorker类是处理耗时任务的工具,它允许在后台线程执行计算,同时在主线程更新UI。这样可以避免UI冻结,提升用户...
因此,你需要使用SwingUtilities的invokeLater或invokeAndWait方法来确保UI更新的正确性。 通过深入学习和实践Java Swing,你可以创建出高效、美观的桌面应用程序。本教程中的实例代码将帮助你更好地掌握Swing的...
当收到新消息时,通过Swing的异步方法(如SwingUtilities.invokeLater或invokeAndWait)在EDT中更新JTextArea的内容。 5. **错误处理**:良好的错误处理是任何程序的关键部分。需要捕获和处理可能的异常,例如网络...
为了确保UI更新操作能够在正确的线程中执行,Swing提供了一些方法,例如`SwingUtilities.invokeLater()`和`SwingUtilities.invokeAndWait()`。 2. **正确使用方法**: - 使用`SwingUtilities.invokeLater()`来异步...
为了确保界面的响应性和避免竞态条件,开发者需要使用SwingUtilities的invokeLater或invokeAndWait方法。 8. **Swing的高级特性**: Swing还提供了对话框(JOptionPane)、拖放支持、国际化和本地化、以及对...
可以使用 SwingUtilities.invokeLater 或 invokeAndWait 方法确保代码在正确的线程中执行。 10. **国际化(Internationalization)**:Swing 支持多语言应用,允许开发者轻松地为不同地区提供本地化版本。 深入...
SwingUtilities提供了很多实用方法,如invokeLater和invokeAndWait,用于在事件调度线程中执行代码,确保GUI的正确更新。 此外,Swing还提供了JDesktopPane和JInternalFrame来实现MDI(多文档界面)应用程序,以及...
- 使用`SwingUtilities.invokeLater`或`invokeAndWait`方法将任务添加到EDT队列,确保在UI线程中安全地执行代码。 当事件发生时,EDT按照以下流程处理: 1. 事件调度线程从事件队列中取出事件。 2. 调用事件源组件...
- **SwingUtilities.invokeLater和SwingUtilities.invokeAndWait**:这两个方法用于确保Swing组件只能在EDT中更新。`invokeLater`会在EDT的末尾执行指定的任务,而`invokeAndWait`则会阻塞当前线程,直到指定的任务...