分享

Swing单线程开发引起的问题

 醉三郎 2012-06-12
Java是一种多线程编程语言。多线程给程序带来了并发的好处。Swing单线程开发的一个问题是,如果在“事件派发线程”上执行的运算太多,那么GUI界面就会停住,系统响应和运算就会非常缓慢。
既然,“事件派发线程”是为了处理GUI事件而设的,那么,我们只应该把GUI事件处理相关的代码,放在“事件派发线程”中执行。其他与界面无关的代码,应该放在Java其他的线程中执行。
这样,我们在Swing的事件处理中,仍然使用Swing的单线程编程模型,而其他业务操作均使用多线程编程模型,这就可以大大提高Swing程序的响应和运行速度,充分运用Java多线程编程的优势。
 
Swing程序的线程
Swing应用程序的线程,分为两种,一种是“事件派发线程”,实际上只有唯一的一条线程;另一种是一般的Java线程,可以有无数条线程。
与系统事件处理相关的代码,需要运行在“事件派发线程”中。一般就是Swing的UI组件。Swing组件,由于包含了SwingUI组件,所以常常也需要运行在“事件派发线程”中。
与业务相关的代码,特别是大计算量,或者涉及到IO,网络,等待资源等耗时的操作,需要放置到一个独立的Java线程中,实现并行运算,提高性能。
 
Swing程序线程应用示例
下面,我以一个一般的Swing程序为例,具体说明Swing多线程编程应该怎样进行。
1,JFrame子类:
public class DiagramDesignerJFrame extends javax.swing.JFrame {…}
这是一个JFrame的子类,是一个顶级窗口。顶级窗口,就是一个从操作系统中拿到的窗口。Java可以在这个窗口中使用从操作系统得到的画笔,绘制出Swing需要的GUI
 
2,main方法:
 
    /**
     *@paramargs
     *            thecommandlinearguments
     */
    public static void main(String args[]) {
        /**
         *在一般线程中,执行SPring容器的初始化
         */
        try {
            SpringUtil.getCtx();
        } catch (BeansException e) {
            /*
            *
            */
            e.printStackTrace();
        } catch (DocumentException e) {
            /*
            *
            */
            e.printStackTrace();
        }
        java.awt.EventQueue.invokeLater(new Runnable() {
            publicvoid run() {
                new DiagramDesignerJFrame().setVisible(true);
            }
        });
    }
 
首先,我们在一般Java线程中,执行Spring容器的初始化。这是非常大量的计算,而且与操作系统事件根本没有关系。
然后,我们在“事件派发线程”中异步执行对JFrame的子类的实例化和可视化。
new DiagramDesignerJFrame()这个实例化操作,是在“事件派发线程”中执行的;
setVisible(true)也是在“事件派发线程”中执行的。
 
 
控制器中多线程的运用
Swing是MVC模式设计的典范。其控制器,就是事件监听器,一般实现为内部类。Swing各个组件都可以注册非常多的事件监听器,也就是“控制器”。
Swing的UI组件,是其界面View。各个Swing组件的Model是其模型。
一般,用户在Swing的UI组件上执行操作,激发事件,然后由控制器进行处理,修改Swing组件的Model。Model又激发Java事件(而非操作系统事件),使UI根据新的Model值重绘。
我们在“控制器”中会调用业务代码,这些代码可能会很耗时。调用结束以后,常常又会调用Swing组件的方法,更新Swing应用程序的外观。
因此,我们应该在Swing的控制器中,把调用业务方法的代码,交给一个新建的一般Java线程去执行。执行完毕之后,再在“事件派发线程”中执行Swing组件更新代码。
    下面是执行“另存为”功能的控制器。它首先保存Swing组件中的数据,然后打开文件选择其对话框,让用户选择要保存到哪个文件中。
/**
     *@returnsaveAsActionListener
     */
    public ActionListener getSaveAsActionListener() {
        if (this.saveAsActionListener == null) {
            this.saveAsActionListener = new ActionListener() {
                /**
                 *响应点击另存为按钮的事件的方法
                 */
                public void actionPerformed(ActionEvent e) {
                    final SwingWorker worker = new SwingWorker() {
 
                        @Override
                        public Object construct() {
                            /*
                            *
                            */
 
                        try {
                            getJEditorPane1().fireControllerChangeListener();
                             return DiagramDesignerJFrame.serviceFinished;
                        } catch (DocumentException e1) {
                            /*
                             *
                             */
                            e1.printStackTrace();
                            JOptionPane.showMessageDialog(
                                    DiagramDesignerJFrame.this, "您的输入不符合xml格式要求!"
                                            + e1.getMessage());
                        } catch (Exception e1) {
                            /*
                             *
                             */
                            e1.printStackTrace();
                        }
                            returnnull;
                        }
                        /**
                         *执行完构造器后,在GUI上异步执行它。(三郎曰,所谓异步就是非阻塞的,立即返回结果。)
                         */
                         public void finished() {
                             saveAction();
                            }
                       
                    };
                    worker.start();
                   
               
 
                   
 
                }
 
            };
 
        }
 
        returnsaveAsActionListener;
    }
 
SwingWorker这个Swing多线程开发的助手类
上面的例子中用到了SwingWorker这个Swing多线程开发的助手类。在JDK6中,这个类已经作为Swing的一部分。这里,我使用的是JDK5,因此,我是使用了之前SUN创建的SwingWorker类。
其中,public Object construct() {}
这个方法中的代码,运行在一个新建的一般Java线程中。我们把耗时的业务方法放在这个方法中执行。
public void finished() {}这个方法中的代码,运行在“事件派发线程”中,是立即返回的。saveAction()显示了一个文件选择其对话框,应该在这里执行!
只有当construct()执行完毕后,才会执行finished()方法。
 
 
结语
    上面就是关于Swing多线程开发的一些论述。注意到Swing单线程事件队列开发模型这个事实,你就能够合理的把代码分配到一般Java线程和Swing事件派发线程中,提高Swing应用的性能。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多