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(java.lang.Runnable)


invokeAndWait

public static void invokeAndWait(Runnable runnable)
                          throws InterruptedException,
                                 InvocationTargetException

导致 runnablerun 方法在 EventQueue 的指派线程上被调用。在所有挂起事件被处理后才发生。在这发生之前调用被阻塞。如果从事件指派线程进行调用,则该方法将抛出 Error。

参数:

runnable - Runnable,其 run 方法应该在 EventQueue 上同步执行

抛出:

InterruptedException - 如果另一个线程已经中断了该线程

InvocationTargetException - 如果运行 runnable 时抛出一个 throwable

从以下版本开始:

1.2

另请参见:

invokeLater(java.lang.Runnable)


设计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

 

时间: 2024-08-31 11:38:18

Swing多线程编程(转)的相关文章

Java Swing多线程死锁问题解析

在基于Java Swing进行图形界面开发的时候,经常遇到的就是Swing多线程问题.我们可以想想一下, 如果需要在一个图形界面上显示很多数据,这些数据是经过长时间.复杂的查询和运算得到的.如果在图 形界面的同一个线程中进行查询和运算工作则会导致一段时间界面处于死机状态,这会给用户带来不良的 互动感受.为了解决这个问题,一般会单独启动一个线程进行运算和查询工作,并随时更新图形界面.这 时候,另一个问题就出现了,可能不仅没有解决原来偶尔死机问题,还可能导致程序彻底死掉.幸运的是 在JDK中暗藏了一

java多线程编程实现下雪效果_java

没有直接采用继承Thread类或者继承Runnable的接口来实现多线程,而是使用了匿名内部类. 要导入的类: import javax.swing.*; import java.awt.*; 1.定义SowPanel类,继承JPanel类,这个类有两个整型数组成员,用来保存雪花起始位置.在构造函数中为数组赋初值:重写父类的paint()方法:定义一个启动多线程的startSnow()方法. class SnowPanel extends JPanel { //定义整型数组,存储雪花坐标 pri

.Net下的多线程编程

多线程是很多驾驭体系所具有的特性,它能大大提升程序的运行效率,因此多线程编程技艺为编程者遍及关切.眼前微软的.Net战略正进一步推进,各种相干的技艺正为盛大编程者所接纳,同样在.Net中多线程编程技艺具有 ... 多线程是很多驾驭体系所具有的特性,它能大大提升程序的运行效率,因此多线程编程技艺为编程者遍及关切.眼前微软的.Net战略正进一步推进,各种相干的技艺正为盛大编程者所接纳,同样在.Net中多线程编程技艺具有相称首要的地位.本文我就向大众介绍在.Net下进行多线程编程的基本办法和程序.开始

有趣的多线程编程(1)——一个简单的例子

编程|多线程 //HelloWordThread.cs//------------------------ using System;using System.Threading;public class Test{ static void Main() { ThreadStart job = new ThreadStart(ThreadJob); Thread thread = new Thread(job); thread.Start(); for (int i=0; i < 5; i++)

有趣的多线程编程(3)——线程内部是如何进行的?

编程|多线程 看一下以下两个例子的运行结果://TestThread.csusing System;using System.Threading;public class Test{ static int count=0; static void Main() { ThreadStart job = new ThreadStart(ThreadJob); Thread thread = new Thread(job); thread.Start(); for (int i=0; i < 5; i

有趣的多线程编程(4)——死锁

编程|多线程 // DeadLockSample.cs// 分析一下为什么会发生死锁? using System;using System.Threading;public class Test{ static readonly object firstLock = new object(); static readonly object secondLock = new object(); static void Main() { new Thread(new ThreadStart(Thre

Java多线程编程详解

编程|多线程|详解 一:理解多线程多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立. 线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单.多个线程的执行是并发的,也就是在逻辑上"同时",而不管是否是物理上的"同时".如果系统只有一个CPU,那么真正的"同时"是不可

C#中使用多线程编程之线程池

编程|多线程 1.     引言 近来在研究C#多线程编程碰到了线程池的概念.不懂,我搜,于是在MSDN和CSDN上寻寻觅觅一番终于搞明白,"缘"来如此,安装本人理解修改后写下这篇文章,希望对后来者有所帮助.   2.     线程池的概念 可以使用线程池来根据应用程序的需要更为有效地利用多个线程.许多应用程序使用多个线程,但这些线程经常在休眠状态中耗费大量的时间来等待事件发生,编程者手动管理多个线程也是一件比较麻烦的事情.事实上,使用线程池就是为应用程序提供一个由系统管理的辅助线程池

C#如何进行多线程编程

编程|多线程 初次接触C#时做的一个小例子,先贴到这吧.由于多线程编程非常复杂,这个小例子只能算是一个入门线的知识点吧 首先建一个应用程序项目,命名为ThreadExample,在窗体上放一个文本框(textBox1) ,一个标签(lblResult),再放两个按钮,分别命名为btnStart.btnStop. 窗体代码: namespace ThreadExample...{    partial class ThreadExample    ...{        /**//// <summ