正确理解和使用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类创建和管理任务线程的例子,描述了如何避免编写运行缓慢、感觉迟钝、容易失去响应的用户界面。这个演示例子叫Image Search,它展示了如何使用SwingWorker API来和网站Flickr进行交互、搜索并下载图像。
如果需要理解Swing Ui的基本概念,包括事件处理和侦听等UI编程,可以参照前面的文章,或从Sun官方网站下载阅读Java教程的Swing部分。
演示程序介绍
Image Search执行的耗时任务是访问Flickr网站服务,该任务不应该在EDT上执行。Image Search程序搜索Flickr站点,搜索匹配用户输入的查询条件的图像,下载匹配图形的缩略图。当用户从缩略图列表中选择某缩略图时,它将下载该图的原始图片。该演示程序使用SwingWorker类作为任务线程,从而避免了在EDT上执行这些耗时任务。
当用户输入查询条件时,程序在Flickr网站请求一个图像查询。如果有符合查询条件的图像,程序下载上限为100个的缩略图像。可以修改程序改变下载图像的数目。搜索和下载图像的同时,有一进度条显示搜索进度。图1显示了查询字段和进度条:
图1,搜索图像并显示下载进度
每当程序成功下载一个缩略图片后,就添加到一个JList组件中,图片从Flickr站点到达后就被添加列表中。程序使用SwingWorker的一个实例,程序能在每个图片到达时添加到列表,而不用等待所有的图片都到达。图2显示列表中的图片:
每当程序成功下载一个缩略图片后,就添加到一个JList组件中,图片从Flickr站点到达后就被添加列表中。程序使用SwingWorker的一个实例,程序能在每个图片到达时添加到列表,而不用等待所有的图片都到达。图2显示列表中的图片:
图2匹配缩略图的列表
当从列表选择一个图片,程序将下载该图片的原始图片,并显示在列表的下面。当大图片下载时,另一进度条将显示下载进度。图3显示列表和图片下载进度条。
当从列表选择一个图片,程序将下载该图片的原始图片,并显示在列表的下面。当大图片下载时,另一进度条将显示下载进度。图3显示列表和图片下载进度条。
图3选中缩略图下载大图片
最后,当所有图片数据下载完毕后,程序在列表下方显示图片。
程序使用SwingWorker来完成所有图片搜索和下载任务。另外,程序还演示了如何取消任务,如何在任务完成之前获得即时结果。该程序有两个SwingWorker的子类:ImageSearcher和ImageRetriever。ImageSearcher类负责搜索和获取图片列表中的缩略图,ImageRetriever类负责用户从列表选择时下载原始版本的图片。本文用这个类来描述SwingWorker类的主要功能。图4显示程序的整个外观。
最后,当所有图片数据下载完毕后,程序在列表下方显示图片。
程序使用SwingWorker来完成所有图片搜索和下载任务。另外,程序还演示了如何取消任务,如何在任务完成之前获得即时结果。该程序有两个SwingWorker的子类:ImageSearcher和ImageRetriever。ImageSearcher类负责搜索和获取图片列表中的缩略图,ImageRetriever类负责用户从列表选择时下载原始版本的图片。本文用这个类来描述SwingWorker类的主要功能。图4显示程序的整个外观。
图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密集型操作。
一个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 {
...
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界面应该如下:
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方法!很容易理解,这样做会造成线程竞争,程序就会陷入死锁。
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操作时间:
幸运的是,不会仅仅因为有复杂的任务、计算或输入输出密集任务需要作为GUI事件处理任务执行,Swing的性能就要有所降低。毕竟有许多桌面程序执行耗时任务,比如处理电子表格公式、跨越网络查询数据库、通过Internet向其他程序发送信息。即使有这些任务,界面仍然可以让用户感觉到响应灵活、快捷。编写响应灵活的程序需要创建和管理独立于EDT的线程。
在Image Search程序中,有两个事件如果完全在EDT上处理就会降低界面的响应速度:图像搜索处理和选中图片下载处理。
两个事件处理都要访问Web服务,这些服务通常要许多秒后才能响应,在此期间,如果程序在EDT上进行Web服务交互,用户就不能取消搜索或者同界面交互,像这两种都不应该在EDT上运行。
图5显示了在A和B点之间,EDT不能处理UI事件,AB两点之间代表了程序访问Flickr网站Web服务的IO操作时间:
图5. 在执行Web服务期间EDT不能响应UI事件
javax.swing.SwingWorker类是Java SE 6中新出现的类,使用SwingWorker,程序能启动一个任务线程来异步查询,并马上返回EDT线程。图6显示了使用SwingWorker后,事件处理立即返回,允许EDT继续执行后续的UI事件。
图6.使用任务线程,程序能够在避免在EDT上执行I/O密集型任务
(待续)
相关推荐
swing swingworker wingworker wingworker
Swing 线程的深入理解和 SwingWorker 基础知识介绍 Swing 是 Java 中的一种图形用户界面(GUI)工具包,用于创建桌面应用程序。然而,在使用 Swing 时,开发者经常会遇到线程相关的问题。本文将深入了解 Swing 线程...
Swing线程的深入理解和SwingWorker基础知识介绍 Swing线程是Java程序设计中的一种重要概念,用于处理图形用户界面(GUI)中的线程问题。在本文中,我们将深入探讨Swing线程的理解和SwingWorker基础知识,并通过实例...
这篇博客“使用SwingWorker异步加载JTree”详细介绍了如何利用SwingWorker来加载大型数据集到JTree组件,避免阻塞UI。 首先,我们需要了解JTree。JTree是Swing中的一个组件,它用来展示树形结构的数据,常用于文件...
SwingWorker是Java Swing库中的一个关键组件,用于在后台线程执行耗时操作,同时保持用户界面(UI)的响应性。这个组件是解决Java GUI应用中多线程问题的有效工具,特别是在需要进行大量计算或者I/O操作时。在这个...
Swt一般情况不允许在另外一个线程中更新控件内容,此程序使用java swing的SwingWorker对swt的控件进行操作更新。对于喜欢使用SwingWorker更新Swing控件的人来说,也可以使用SwingWorker更新Swt控件
Java的Swing编程中,SwingWorker线程模式和顶层容器是构建响应式图形用户界面(GUI)的关键组件。在Swing中,正确地管理线程对于确保应用程序的性能和用户体验至关重要。以下是对这些概念的详细说明: 1. **初始...
8. **Swing Worker**:为了在用户界面线程(Event Dispatch Thread,EDT)之外执行耗时任务,Swing提供了SwingWorker类。这样可以防止长时间运行的任务阻塞用户界面,保持应用的响应性。 综上所述,“浅析使用模型...
SwingWorker的替代品,用于在Swing中执行异步任务。 我们将SwingWorker.java与Action接口合并,生成AbstractAsynchronousAction.java。 此类的具体扩展可以用来代替任何Action。
SwingWorker, Void> worker = new SwingWorker, Void>() { @Override protected Void doInBackground() throws Exception { // 执行耗时任务 Thread.sleep(5000); // 模拟耗时操作 return null; } @Override...
本实验报告是基于互联网程序设计课程的第二个实验,主要目标是理解并掌握服务器一客户一线程通用技术框架和 SwingWorker,V> 后台线程技术。 一、实验目的: 本实验的主要目的有两个:(1)理解并掌握服务器一客户...
标题中的“ProgressBar 2种经典实现”指的是在编程中如何创建和使用进度条控件,通常在用户界面(UI)中显示任务执行的进度。在软件开发中,进度条是提高用户体验的重要元素,因为它可以向用户反馈程序正在执行的...
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工人故障排除 在这里,我们有一个简单的Swing应用程序,现在需要添加一个耗时的任务,并且需要在运行时更新进度条。 当前解决方案完全在EDT上运行( AnalysisService类中100%打包),因此阻止进度条被更新/...
`SwingWorker`类继承自`Worker`抽象类,并实现了`SwingWorker, V>`接口,其中`T`表示worker线程产生的中间结果类型,而`V`则表示worker线程完成后返回的结果类型。 `SwingWorker`的关键方法包括: - `execute()`:...
SwingWorker, Integer> worker = new SwingWorker, Integer>() { @Override protected Void doInBackground() throws Exception { // 下载逻辑,包括网络请求和数据读写 // 每隔一段时间publish进度值 return ...
实验的目标是掌握SwingWorker类的应用,理解端口扫描的编程技巧,并进一步熟悉Swing界面设计。 SwingWorker类在Java中是一个用于在后台线程执行耗时操作的工具类,它允许在不影响用户界面的情况下进行计算,从而...
解决此问题的常用方法是使用Swing提供的异步机制,如`SwingUtilities.invokeLater()`或`SwingWorker`。这些工具允许开发者将耗时任务放在后台线程中执行,同时保持用户界面的响应性。然而,这些解决方案的正确实施...
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....
5. **SwingWorker**:SwingWorker是Swing为多线程设计的类,它可以异步执行任务并在后台线程中更新界面。在doInBackground()方法中执行耗时操作,然后在process()或done()方法中更新进度条。 ```java SwingWorker, ...