很多学JAVA程序员都是从Swing开始的,但很多人对AWT GUI线程的机制并没有太深的了解,或者说一直都只了解线程的概念,而不了解AWT对线程的使用。我发现很多人碰到线程阻塞的问题,就通过调用 SwingUtilities.invokeLater()来解决。
目前成都创新互联公司已为成百上千家的企业提供了网站建设、域名、网页空间、网站托管运营、企业网站设计、徐汇网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。
其实这是很容易造成误会的地方:
问题代码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的场景,也是设计这个工具的目的。
原文链接:http://seaman.iteye.com/blog/608584
【编辑推荐】
分享题目:Swing多线程编码过程中的误区
路径分享:http://www.shufengxianlan.com/qtweb/news37/27087.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联