`
seaman
  • 浏览: 19632 次
  • 性别: Icon_minigender_1
  • 来自: 珠海
最近访客 更多访客>>
社区版块
存档分类
最新评论

Swing多线程编码过程中的误区(SwingUtilities)

阅读更多
[笔记]
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的场景,也是设计这个工具的目的。



1
1
分享到:
评论

相关推荐

    Swing 线程之SwingUtilities invokeLater docx

    Swing 线程之SwingUtilities invokeLater docx

    Swing线程之SwingUtilities.invoke

    Swing线程之SwingUtilities.invokeLater解释Java开发Java经验技巧共7页.pdf.zip

    Java Swing界面的文本编辑器源代码.rar

     Swing线程在同一时刻仅能被一个线程所访问。一般来说,这个线程是事件派发线程(event-dispatching thread)。 如果需要从事件处理(event-handling)或绘制代码以外的地方访问UI,那么可以使用SwingUtilities类的...

    java Swing的使用.docx

    在main方法中,我们使用SwingUtilities.invokeLater方法在事件分派线程中创建了一个MyFrame对象。这是为了确保Swing应用程序在正确的线程中运行,避免出现并发访问问题。 Swing还提供了许多其他的GUI组件和功能,如...

    基于Swing的打砖块游戏的Java程序

    程序通过创建Ball、Paddle和Block类来实现游戏的运行逻辑,其中...程序的入口 main 方法通过调用 SwingUtilities.invokeLater 方法来创建一个线程并运行 BreakBlockGame 类,以避免在主线程中创建和显示 Swing 组件。

    基于swing的java猜数字游戏代码.zip

    在main()方法中,通过SwingUtilities.invokeLater()方法启动主界面,确保界面在事件分派线程(Event Dispatch Thread)中创建和显示。 注意:此示例只是一个简单的演示,没有包括输入验证和错误处理等完整的功能。...

    简单的排球比赛计分系统,使用Java Swing实现

    程序创建一个窗口,包含输入分数的文本框、计分按钮、查询按钮、显示比分的文本区域和退出按钮。用户可以通过输入分数进行计分,通过查询按钮...最后,通过SwingUtilities.invokeLater方法确保在正确的线程中运行程序。

    用java实现的一个俄罗斯方块游戏

    在方块下降的过程中,检查是否可以继续下降,如果不行,则将方块固定并消除一行方块,同时更新得分。方块下降过程中,还可以旋转方块。最后,创建一个SwingUtilities.invokeLater方法来延迟执行创建TetrisGame对象的...

    Java制作多种风格的窗口界面一例.rar

    Java制作多种风格的窗口界面一例,虽然这种界面风格不是太漂亮,但展示了如何实现多风格窗口的变化,如果你的美工不错,可以设计出更漂亮的窗口来,搭配本源码中演示的方法,可实现多窗口风格的切换。  //改变窗口...

    项目源码-java酒店管理系统

    import javax.swing.SwingUtilities; import javax.swing.UIManager; import com.mwq.frame.LandFrame; public class DrinkeryManage { public DrinkeryManage() { // Center the window Toolkit toolkit = ...

    java日期时间选择

    在一个项目中要是用到swing日期控件,网上找了好久都没有找到现成的。 最后根据老外的java日期选择控件做了一点封装,加上了时间选择。 public static void main(String[] args){ try { JFrame frame = new ...

    MUtilities:MATLAB 访问选定的 Java SwingUtilities、AWTUtilities 和 HG 对象的类似函数-matlab开发

    MUtilities 是一个 MATLAB 类,它提供一组静态方法,类似于 Java SwingUtilities 和新的 AWTUtilities 集合中的一些方法,并包括其中的一些方法。 -------------------------------------------------- -----------...

    java pdf 查看器

    import javax.swing.SwingUtilities; import com.sun.pdfview.action.GoToAction; import com.sun.pdfview.action.PDFAction; import java.lang.reflect.InvocationTargetException; /** * A demo PDF Viewer ...

    贝叶斯主观推理算法java源码

    import javax.swing.SwingUtilities; import javax.swing.UIManager; import java.awt.Dimension; /** * <p>Title: * * <p>Description: * * <p>Copyright: Copyright (c) 2010 * * <p>Company: * * @...

    Java 语言基础 —— 非常符合中国人习惯的Java基础教程手册

    Java 中你可以凭借多态性,通过一个画圆的对象,来创建一个画椭圆或矩形的对象。不管是 画圆,画椭圆还是画矩形的方法,它们都有一个相同的方法名,但以不同的方式完成他们的 画圆的功能。 1.8 类和对象 1.8.1 ...

    substance5.3已编译

    Substance (https://substance.dev.java.net)有很多的外观可以选择,而且都很漂亮,还提供了强大的API供开发者使用。 在设置外观时请把UIManager.set...SwingUtilities.invokeLater(new Runnable() {}的run方法中。

    Substance5.3已编译的jar文件

    Substance (https://substance.dev.java.net)有很多的外观可以选择,而且都很漂亮,还提供了强大的API供...SwingUtilities.invokeLater(new Runnable() {}的run方法中。 这个上传错了,可是我不知道怎么删,唉。

    java课程设计学生信息管理系统(1).doc

    " "public static void main(String[] args) { " "SwingUtilities.invokeLater(new Runnable() { " "public void run() { " "StudentJFrame inst = new StudentJFrame(); " "inst.setLocationRelativeTo(null); " ...

    Substance--JAVA的皮肤更换包

    Property name for specifying skin to be used on the specific root pane. This property can only... After setting this property, call SwingUtilities#updateComponentTreeUI(Component) on the matching window.

    substance.jar

    JFrame.setDefaultLookAndFeelDecorated(true); JDialog.setDefaultLookAndFeelDecorated(true); java.awt.EventQueue.invokeLater(new Runnable() { ...SwingUtilities.updateComponentTreeUI(comm); }// end if

Global site tag (gtag.js) - Google Analytics