`
javahacker2
  • 浏览: 43522 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

浅析SwingWorker

阅读更多
        正确理解和使用Swing线程模型编程是编写响应灵活的Swing程序的关键。从Java SE 6开始引进的SwingWorker能帮你轻松的编写多线程Swing程序,改善你Swing程序的结构,提高界面响应的灵活性。SDN(Sun developer Network)上有一篇很好的文章:Improve Application Performance With SwingWorker in Java SE 6详细演示了如何使用SwingWorker改善Swing应用程序。把它翻译过来同大家共享。
摘要
        桌面应用程序员常见的错误是误用Swing事件调度线程(Event Dispatch Thread, EDT)。他们要么从非UI线程访问UI组件,要么不考虑事件执行顺序,要么不使用独立任务线程而在EDT线程上执行耗时任务,结果使编写的应用程序变得响应迟钝、速度很慢。耗时计算和输入/输出(IO)密集型任务不应放在Swing EDT上运行。发现这种问题的代码并不容易,但Java SE 6提供了javax.swing.SwingWorker类,使修正这种代码变得更容易。
        本文演示了一个使用SwingWorker类创建和管理任务线程的例子,描述了如何避免编写运行缓慢、感觉迟钝、容易失去响应的用户界面。这个演示例子叫Image Search,它展示了如何使用SwingWorker API来和网站Flickr进行交互、搜索并下载图像。
        如果需要理解Swing Ui的基本概念,包括事件处理和侦听等UI编程,可以参照前面的文章,或从Sun官方网站下载阅读Java教程的Swing部分
演示程序介绍
        Image Search执行的耗时任务是访问Flickr网站服务,该任务不应该在EDT上执行。Image Search程序搜索Flickr站点,搜索匹配用户输入的查询条件的图像,下载匹配图形的缩略图。当用户从缩略图列表中选择某缩略图时,它将下载该图的原始图片。该演示程序使用SwingWorker类作为任务线程,从而避免了在EDT上执行这些耗时任务。
        当用户输入查询条件时,程序在Flickr网站请求一个图像查询。如果有符合查询条件的图像,程序下载上限为100个的缩略图像。可以修改程序改变下载图像的数目。搜索和下载图像的同时,有一进度条显示搜索进度。图1显示了查询字段和进度条:
使用SwingWorker之一
图1,搜索图像并显示下载进度
        每当程序成功下载一个缩略图片后,就添加到一个JList组件中,图片从Flickr站点到达后就被添加列表中。程序使用SwingWorker的一个实例,程序能在每个图片到达时添加到列表,而不用等待所有的图片都到达。图2显示列表中的图片:
使用SwingWorker之一
图2匹配缩略图的列表
        当从列表选择一个图片,程序将下载该图片的原始图片,并显示在列表的下面。当大图片下载时,另一进度条将显示下载进度。图3显示列表和图片下载进度条。
使用SwingWorker之一
图3选中缩略图下载大图片
        最后,当所有图片数据下载完毕后,程序在列表下方显示图片。
        程序使用SwingWorker来完成所有图片搜索和下载任务。另外,程序还演示了如何取消任务,如何在任务完成之前获得即时结果。该程序有两个SwingWorker的子类:ImageSearcher和ImageRetriever。ImageSearcher类负责搜索和获取图片列表中的缩略图,ImageRetriever类负责用户从列表选择时下载原始版本的图片。本文用这个类来描述SwingWorker类的主要功能。图4显示程序的整个外观。
使用SwingWorker之一
 
图4使用SwingWorkere类创建响应灵活程序界面
回顾Swing线程基础
        一个Swing程序中一般有下面三种类型的线程:
    * 初始化线程(Initial Thread)
    * UI事件调度线程(EDT)
    * 任务线程(Worker Thread)
        每个程序必须有一个main方法,这是程序的入口。该方法运行在初始化或启动线程上。初始化线程读取程序参数并初始化一些对象。在许多Swing程序中,该线程主要目的是启动程序的图形用户界面(GUI)。一旦GUI启动后,对于大多数事件驱动的桌面程序来说,初始化线程的工作就结束了。
        Swing程序只有一个用EDT,该线程负责GUI组件的绘制和更新,通过调用程序的事件处理器来响应用户交互。所有事件处理都是在EDT上进行的,程序同UI组件和其基本数据模型的交互只允许在EDT上进行,所有运行在EDT上的任务应该尽快完成,以便UI能及时响应用户输入。
        Swing编程时应该注意以下两点:
1.从其他线程访问UI组件及其事件处理器会导致界面更新和绘制错误。
2.在EDT上执行耗时任务会使程序失去响应,这会使GUI事件阻塞在队列中得不到处理。
3.应使用独立的任务线程来执行耗时计算或输入输出密集型任务,比如同数据库通信、访问网站资源、读写大树据量的文件。
        总之,任何干扰或延迟UI事件的处理只应该出现在独立任务线程中;在初始化线程或任务线程同Swing组件或其缺省数据模型进行的交互都是非线程安全性操作。
        SwingWorker类帮你管理任务线程和Swing EDT之间的交互,尽管SwingWorker不能解决并发线程中遇到的所有问题,但的确有助于分离Swing EDT和任务线程,使它们各负其责:对于EDT来说,就是绘制和更新界面,并响应用户输入;对于任务线程来说,就是执行和界面无直接关系的耗时任务和I/O密集型操作。
使用合适线程
        初始化线程运行程序的main方法,该方法能处理许多任务。但在典型的Swing程序中,其主要任务就是创建和运行应用程序的界面。创建UI的点,也就是程序开始将控制权转交给UI时的点,往往是同EDT交互出现问题的第一个地方。
        Image Search示例的主类是MainFrame,从其main方法启动。许多程序使用下面方法启动界面,但这是错误的启动UI界面的方法:
public class MainFrame extends javax.swing.JFrame {
  ...
  public static void main(String[] args) {
    new MainFrame().setVisible(true);
  }
}

        尽管这种错误出现在开始,但仍然违反了不应在EDT外的其他线程同Swing组件交互的原则。这个错误尤其容易犯,线程同步问题虽然不是马上显示出来,但是还要注意避免这样书写。
        正确启动UI界面应该如下:
public class MainFrame extends javax.swing.JFrame {
  ...
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        new MainFrame().setVisible(true);
      }
    });
  }
}

        使用NetBeans IDE的开发者应该对这段代码很熟悉,NetBeans通常会自动生成这段代码。这段启动代码虽然和SwingWorker没有直接关系,但是这个编程范式很重要。SwingUtilities类包含一些静态方法帮你同UI组件交互,其中invokeLater方法意思是在EDT上执行其Runnable任务。Runnable接口定义了可作为独立线程执行的任务。
        在初始化线程中使用invokeLater方法能正确的初始化程序界面。就像前面文章所提到的,此方法是异步执行的,也就是说调用会立即返回。创建界面后,大部分初始化线程基本上就结束了。
        通常有两种办法调用此方法:
    * SwingUtilities.invokeLater
    * EventQueue.invokeLater
        两个方法都是正确的,选择任何一个都可以。实际上,SwingUtilities版只是一个薄薄的封装方法,它直接转而调用EventQueue.invokeLater。因为Swing框架本身经常调用SwingUtilities,使用SwingUtilities可以减少程序引入的类。
        另种将任务放到EDT执行的方法是SwingUtilities.invokeAndWait,不像invokeLater,invokeAndWait方法是阻塞执行的,它在EDT上执行Runnnable任务,直到任务执行完了,该方法才返回调用线程。
        invokeLater和invokeAndWait都在事件派发队列中的所有事件都处理完之后才执行它们的Runnable任务,也就是说,这两个方法将Runnable任务放在事件队列的末尾。
        注意:虽然可以在其他线程上调用invokeLater,也可以在EDT上调用invokeLater,但是千万不要在EDT线程上调用invokeAndWait方法!很容易理解,这样做会造成线程竞争,程序就会陷入死锁。
将EDT线程仅用于GUI任务
        Swing框架负责管理组件绘制、更新以及EDT上的线程处理。可以想象,该线程的事件队列很繁忙,几乎每一次GUI交互和事件都是通过它完成。事件队列的上任务必须非常快,否则就会阻塞其他任务的执行,使队列里阻塞了很多等待执行的事件,造成界面响应不灵活,让用户感觉到界面响应速度很慢,使他们失去兴趣。理想情况下,任何需时超过30到100毫秒的任务不应放在EDT上执行,否则用户就会觉察到输入和界面响应之间的延迟。
        幸运的是,不会仅仅因为有复杂的任务、计算或输入输出密集任务需要作为GUI事件处理任务执行,Swing的性能就要有所降低。毕竟有许多桌面程序执行耗时任务,比如处理电子表格公式、跨越网络查询数据库、通过Internet向其他程序发送信息。即使有这些任务,界面仍然可以让用户感觉到响应灵活、快捷。编写响应灵活的程序需要创建和管理独立于EDT的线程。
        在Image Search程序中,有两个事件如果完全在EDT上处理就会降低界面的响应速度:图像搜索处理和选中图片下载处理。
        两个事件处理都要访问Web服务,这些服务通常要许多秒后才能响应,在此期间,如果程序在EDT上进行Web服务交互,用户就不能取消搜索或者同界面交互,像这两种都不应该在EDT上运行。
        图5显示了在A和B点之间,EDT不能处理UI事件,AB两点之间代表了程序访问Flickr网站Web服务的IO操作时间:
使用SwingWorker之一
图5. 在执行Web服务期间EDT不能响应UI事件
        javax.swing.SwingWorker类是Java SE 6中新出现的类,使用SwingWorker,程序能启动一个任务线程来异步查询,并马上返回EDT线程。图6显示了使用SwingWorker后,事件处理立即返回,允许EDT继续执行后续的UI事件。
使用SwingWorker之一
图6.使用任务线程,程序能够在避免在EDT上执行I/O密集型任务
(待续)
分享到:
评论

相关推荐

    Swingworker

    swing swingworker wingworker wingworker

    Swing线程的深入理解和SwingWorker基础知识介绍[参照].pdf

    Swing 线程的深入理解和 SwingWorker 基础知识介绍 Swing 是 Java 中的一种图形用户界面(GUI)工具包,用于创建桌面应用程序。然而,在使用 Swing 时,开发者经常会遇到线程相关的问题。本文将深入了解 Swing 线程...

    Swing线程的深入理解和SwingWorker基础知识介绍

    Swing线程的深入理解和SwingWorker基础知识介绍 Swing线程是Java程序设计中的一种重要概念,用于处理图形用户界面(GUI)中的线程问题。在本文中,我们将深入探讨Swing线程的理解和SwingWorker基础知识,并通过实例...

    使用SwingWorker异步加载JTree

    这篇博客“使用SwingWorker异步加载JTree”详细介绍了如何利用SwingWorker来加载大型数据集到JTree组件,避免阻塞UI。 首先,我们需要了解JTree。JTree是Swing中的一个组件,它用来展示树形结构的数据,常用于文件...

    swingworker排序

    SwingWorker是Java Swing库中的一个关键组件,用于在后台线程执行耗时操作,同时保持用户界面(UI)的响应性。这个组件是解决Java GUI应用中多线程问题的有效工具,特别是在需要进行大量计算或者I/O操作时。在这个...

    SWT与SwingWorker的结合

    Swt一般情况不允许在另外一个线程中更新控件内容,此程序使用java swing的SwingWorker对swt的控件进行操作更新。对于喜欢使用SwingWorker更新Swing控件的人来说,也可以使用SwingWorker更新Swt控件

    Java的Swing编程中使用SwingWorker线程模式及顶层容器

    Java的Swing编程中,SwingWorker线程模式和顶层容器是构建响应式图形用户界面(GUI)的关键组件。在Swing中,正确地管理线程对于确保应用程序的性能和用户体验至关重要。以下是对这些概念的详细说明: 1. **初始...

    浅析使用模型参数构造Java Swing组件.zip

    8. **Swing Worker**:为了在用户界面线程(Event Dispatch Thread,EDT)之外执行耗时任务,Swing提供了SwingWorker类。这样可以防止长时间运行的任务阻塞用户界面,保持应用的响应性。 综上所述,“浅析使用模型...

    An Alternative to SwingWorker.java-开源

    SwingWorker的替代品,用于在Swing中执行异步任务。 我们将SwingWorker.java与Action接口合并,生成AbstractAsynchronousAction.java。 此类的具体扩展可以用来代替任何Action。

    swing不确定进度条

    SwingWorker, Void> worker = new SwingWorker, Void>() { @Override protected Void doInBackground() throws Exception { // 执行耗时任务 Thread.sleep(5000); // 模拟耗时操作 return null; } @Override...

    互联网程序设计实验二0001.docx

    本实验报告是基于互联网程序设计课程的第二个实验,主要目标是理解并掌握服务器一客户一线程通用技术框架和 SwingWorker,V> 后台线程技术。 一、实验目的: 本实验的主要目的有两个:(1)理解并掌握服务器一客户...

    ProcessBar 2种经典实现

    标题中的“ProgressBar 2种经典实现”指的是在编程中如何创建和使用进度条控件,通常在用户界面(UI)中显示任务执行的进度。在软件开发中,进度条是提高用户体验的重要元素,因为它可以向用户反馈程序正在执行的...

    swing-worker-1.1.jar

    Exceptionin thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: org/jdesktop/swingworker/SwingWorker at com.android.draw9patch.Application$1.run(Application.java:48) at java.awt.event....

    swing-worker-troubleshooting:回购以解决SwingWorker的问题

    Swing工人故障排除 在这里,我们有一个简单的Swing应用程序,现在需要添加一个耗时的任务,并且需要在运行时更新进度条。 当前解决方案完全在EDT上运行( AnalysisService类中100%打包),因此阻止进度条被更新/...

    Java开发中的线程安全选择与Swing

    `SwingWorker`类继承自`Worker`抽象类,并实现了`SwingWorker, V>`接口,其中`T`表示worker线程产生的中间结果类型,而`V`则表示worker线程完成后返回的结果类型。 `SwingWorker`的关键方法包括: - `execute()`:...

    swing 下载文件时的进度条

    SwingWorker, Integer> worker = new SwingWorker, Integer>() { @Override protected Void doInBackground() throws Exception { // 下载逻辑,包括网络请求和数据读写 // 每隔一段时间publish进度值 return ...

    互联网程序设计实验六.doc

    实验的目标是掌握SwingWorker类的应用,理解端口扫描的编程技巧,并进一步熟悉Swing界面设计。 SwingWorker类在Java中是一个用于在后台线程执行耗时操作的工具类,它允许在不影响用户界面的情况下进行计算,从而...

    再论JavaSwing线程.pdf

    解决此问题的常用方法是使用Swing提供的异步机制,如`SwingUtilities.invokeLater()`或`SwingWorker`。这些工具允许开发者将耗时任务放在后台线程中执行,同时保持用户界面的响应性。然而,这些解决方案的正确实施...

    Eclipse编写的把网页保存成图片Demo

    import org.dje.jnativeswing.swingworker.SwingWorker; import org.jdesktop.swingx.JXWebBrowser; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.net....

    java 进度条的实现

    5. **SwingWorker**:SwingWorker是Swing为多线程设计的类,它可以异步执行任务并在后台线程中更新界面。在doInBackground()方法中执行耗时操作,然后在process()或done()方法中更新进度条。 ```java SwingWorker, ...

Global site tag (gtag.js) - Google Analytics