zoukankan      html  css  js  c++  java
  • Swing多线程编程(转)

    关键字:
    Swing,多线程,GUI,SwingWorker
    摘要:
    本文论述了怎样开发多线程的Swing程序,从而提高Swing程序的响应速度和性能。
        近期,我将推出一系列研究Swing程序的文章,这也算是为了向Swing这个优秀的GUI库的设计者致敬吧!
    Swing这种优秀的GUI库一直不能占领桌面市场,实在令人费解,今天,我就用我的努力,为java在桌面市场的成功尽我微薄之力吧!
     
     
    Swing的单线程开发机制
    多线程开发,显然要比单线程开发有趣、高效、美妙得多。特别是在Java这种天生支持多线程的语言中,更是如此。可是,Java最重要的组成部分Swing确是单线程的!
    并非只有Swing是单线程的,大多数GUI库都是单线程的。因为,在GUI的事件处理中,事件和处理事件的底层资源是如此的复杂,以至于使用多线程开发,很难避免死锁和资源竞争问题的发生。而且,如果锁定太多系统资源,对GUI的系统性能将会造成消极影响。
    因此,Swing被开发成了一个基于事件队列的单线程编程模型。GUI上的事件,一个个依次在“事件派发线程”上执行,不会发生事件对资源的争夺。
    Java.awt.EventQueue类,就执行这个功能。
    EventQueue 是一个与平台无关的类,它将来自于基础同位体类和受信任的应用程序类的事件列入队列。
    它封装了异步事件指派机制,该机制从队列中提取事件,然后通过对此 EventQueue 调用 dispatchEvent(AWTEvent) 方法来指派这些事件(事件作为参数被指派)。该机制的特殊行为是与实现有关的。指派实际排入到该队列中的事件(注意,正在发送到 EventQueue 中的事件可以被合并)的惟一要求是:
    按顺序。
    也就是说,不允许同时从该队列中指派多个事件。
    指派顺序与它们排队的顺序相同。
    也就是说,如果 AWTEvent A 比 AWTEvent B 先排入到 EventQueue 中,那么事件 B 不能在事件 A 之前被指派。
    一些浏览器将不同基本代码中的 applet 分成独立的上下文,并在这些上下文之间建立一道道墙。在这样的场景中,每个上下文将会有一个 EventQueue。其他浏览器将所有的 applet 放入到同一个上下文中,这意味着所有 applet 只有一个全局 EventQueue。该行为是与实现有关的。有关更多信息,请参照浏览器的文档。
    所有Swing/AWT事件的处理方法,都被放到唯一的“事件派发线程”中执行。
    一般,我们使用EventQueue类的2个方法,将事件处理方法放到“事件派发线程”中执行。
    invokeLater
    public static void invokeLater(Runnable runnable)
    导致 runnablerun 方法在 EventQueue 的指派线程上被调用。在所有挂起事件被处理后才发生。
    参数:
    runnable - Runnable,其 run 方法应该在 EventQueue 上同步执行
    从以下版本开始:
    1.2
    另请参见:

    invokeAndWait
    public static void invokeAndWait(Runnable runnable)
                              throws InterruptedException,
                                     InvocationTargetException
    导致 runnablerun 方法在 EventQueue 的指派线程上被调用。在所有挂起事件被处理后才发生。在这发生之前调用被阻塞。如果从事件指派线程进行调用,则该方法将抛出 Error。
    参数:
    runnable - Runnable,其 run 方法应该在 EventQueue 上同步执行
    抛出:
    InterruptedException - 如果另一个线程已经中断了该线程
    InvocationTargetException - 如果运行 runnable 时抛出一个 throwable
    从以下版本开始:
    1.2
    另请参见:

    设计Swing的UI组件的执行,一般都需要运行在“事件派发线程”上。
     
     
    Swing单线程开发引起的问题
    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
         */
        publicstaticvoid 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() {
                    /**
                     *响应点击另存为按钮的事件的方法
                     */
                    publicvoid 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上异步执行它。
                             */
                             publicvoid finished() {
                                 saveAction();
                                }
                           
                        };
                        worker.start();
                       
                   
     
                       
     
                    }
     
                };
     
            }
     
            returnsaveAsActionListener;
        }
     
    SwingWorker这个Swing多线程开发的助手类
    上面的例子中用到了SwingWorker这个Swing多线程开发的助手类。在JDK6中,这个类已经作为Swing的一部分。这里,我使用的是JDK5,因此,我是使用了之前SUN创建的SwingWorker类。
    其中,public Object construct() {}
    这个方法中的代码,运行在一个新建的一般Java线程中。我们把耗时的业务方法放在这个方法中执行。
    publicvoid finished() {}
    这个方法中的代码,运行在“事件派发线程”中,是立即返回的。saveAction()显示了一个文件选择其对话框,应该在这里执行!
    只有当construct()执行完毕后,才会执行finished()方法。
     
     
    结语
        上面就是关于Swing多线程开发的一些论述。注意到Swing单线程事件队列开发模型这个事实,你就能够合理的把代码分配到一般Java线程和Swing事件派发线程中,提高Swing应用的性能。
     
    http://www.cnblogs.com/armlinux/archive/2007/01/30/2391038.html
     
  • 相关阅读:
    flink 读取kafka 数据,partition分配
    Flink 报错 "Could not find a suitable table factory for 'org.apache.flink.table.factories.StreamTableSourceFactory' in the classpath"
    flume接收http请求,并将数据写到kafka
    【翻译】Flume 1.8.0 User Guide(用户指南) Processors
    【翻译】Flume 1.8.0 User Guide(用户指南) Channel
    【翻译】Flume 1.8.0 User Guide(用户指南) Sink
    【翻译】Flume 1.8.0 User Guide(用户指南) source
    【翻译】Flume 1.8.0 User Guide(用户指南)
    Apache Flink 简单安装
    Java之使用IDE
  • 原文地址:https://www.cnblogs.com/softidea/p/4495844.html
Copyright © 2011-2022 走看看