`

使用Swing Worker线程 --执行后台任务的新方法

阅读更多

本文给出了一些使用SwingWorker类的例子。SwingWorker类的目的是实现一个后台线程,让你可以用它来执行一些费时的操作,而不影响你的程序的GUI的性能。关于SwingWorker类的一些基本信息,请参阅《线程和Swing》。
注意:在2000年9月我们修改了这篇文章和它的例子以适用于一个更新版本的SwingWorker类。SwingWorker类的这个版本修正了一些微妙的线程bug。
对执行一些费时或可阻塞操作的Swing程序来说,线程是基本的解决之道。例如,如果你的应用程序要根据用户选择的菜单项发出一个数据库请求或加载一个文件,那么你应该在一个单独的线程中完成这些工作。本文阐述了在一个分离的worker线程中完成上述工作的途径。
本文包括以下主要内容:

  • SwingWorker类:这一部分告诉你怎样下载SwingWorker类并描述了SwingWorker类的用途。介绍了SwingWorker类的interrupt()方法。
  • 引入Worker线程的例子:演示一个运用SwingWorker类的应用程序。
  • 例一:中断一个Swing Worker线程:解释如何运用interrupt()方法来中断worker线程。
  • 例二:从Swing Worker线程反馈给用户:例一的增强,添加了一个模式对话框以提示用户输入。


概览:SwingWorker类


因为SwingWorker类并不是Java发行版的一部分,你需要下载和编译它才能使用。它的源代码在这里:
SwingWorker.java

SwingWorker类简介


SwingWorker类可以简单且方便地用于在一个新的线程中计算一个数值。要使用这个类,你只要创建一个SwingWorker的子类并覆盖SwingWorker.construct()方法来执行计算。然后你实例化它,并在这个新实例上调用start()方法。
例如,下面的代码片断中产生了一个线程,其中构造了一个字符串。然后,片断中使用了get()方法来取得前面由construct方法所返回的值,并且在必要时将等待。最后,在显示器上显示出字符串的值。

  1. SwingWorker worker = new SwingWorker() {
  2.    public Object construct() {
  3.       return "Hello" + " " + "World";
  4.    }
  5. };
  6. worker.start();
  7. System.out.println(worker.get().toString());


在实际的应用程序中,construct方法会做些更有用(但可能很费时)的事情。比如,它可能做下列工作之一:

  • 执行大量的运算
  • 执行可能导致大量的类被装载的代码
  • 为网络或磁盘I/O阻塞
  • 等待其他资源


在上面的代码片断中没有展示的一个SwingWorker类的特性是,当construct()返回后,SwingWorker可以让你在事件派发线程中执行一些代码。你可以通过覆盖SwingWorker的finished()方法来做到这一点。典型地,你可以用finished()来显示刚刚构造的一些组件或设置组件上显示的数据。
原始版本的SwingWorker类的一个局限是,一旦一个worker线程开始运行,你无法中断它。(译注:本文、本文的例子及SwingWorker类曾被更新。)不管怎么说,对于一个交互应用程序来说,让用户在工作线程完成前一直等待是相当糟糕的风格。如果用户希望终止一项正在执行中的操作,执行此操作的线程应该能够尽快中止。

使用interrupt()方法


在第二版的SwingWorker类中加入了一个interrupt()方法以允许中断一个线程。你的线程应该以下面两种途径之一得到中断的通知:

  • 正在执行诸如sleep()或wait()方法的线程在interrupt()被调用时会抛出一个InterruptedException。
  • 线程可以显式询问它是否已被中断,通过形如以下代码:
    1. Thread.interrupted()
     


使用sleep()或wait()方法的工作线程(如后面例子中的线程)一般不需要显式检查是否被中断。通常让sleep()或wait()方法抛出InterruptedException就足够了。
不过,如果你希望能够中断一个不含定时循环的SwingWorker,还是需要用interrupted()方法来显式检查中断。

引入Worker线程的例子


本文余下的部分讨论一个包含两个worker线程的例子的程序。下图是程序主窗口的截图,和它弹出的一个对话框:


例子的源代码由以下文件组成:


你可以下载一个包含上面所有文件的zip文件。在此zip文件中还包含一个带有HTML格式的说明的例子,这个版本的例子更能说明问题(但也更复杂)。
运行此例子前要先编译:
/usr/local/java/jdk1.3.0/bin/javac ThreadsExample.java
用以下命令行运行此例子:
/usr/local/java/jdk1.3.0/bin/java ThreadsExample
当你按下“Start”按钮,相应例子的worker线程将被创建。你可以在进度条中查看它的进度。你可以按下“Cancel”按钮来中断worker线程。在启动例子二稍等几秒,你会看到一个对话框提示你确认是否执行。稍后我们再详述这个。

例子一:中断Swing Worker线程


接着我们要讨论的例子是Example1.java。这个例子中的worker线程包含一个执行100次的循环,并在两次循环之间睡眠半秒。

  1. //progressBar maximum is NUMLOOPS (100) progressBar的最大值是NUMLOOPS (100)
  2. for(int i = 0; i < NUMLOOPS; i++) {
  3.    updateStatus(i);
  4.    ...
  5.    Thread.sleep(500);
  6. }


为了向你展示如何使用interrupt()方法,我们的例子程序可以让你启动一个SwingWorker然后等待它完成或者中断它。程序中的SwingWorker子类的construct()方法所作的唯一一件事就是调用Example1的doWork()方法。doWork()方法的完整源码列在下面。这个例子在处理worker线程时会重置进度条并把标签设为“Interrupted”。因为中断可能发生在sleep()方法调用之外,所以代码中在调用sleep()方法之前要先检查中断与否。

  1. Object doWork() {
  2.    try {
  3.       for(int i = 0; i < NUMLOOPS; i++) {
  4.          updateStatus(i);
  5.          if (Thread.interrupted()) {
  6.             throw new InterruptedException();
  7.          }
  8.          Thread.sleep(500);
  9.       }
  10.    }
  11.    catch (InterruptedException e) {
  12.       updateStatus(0);
  13.       return "Interrupted";  
  14.    }
  15.    return "All Done"
  16. }


在此方法中执行的费时操作应该:周期性地让用户知道它有所进展。updateStatus()方法会为事件派发线程排队Runnable对象(记住:不要在其他线程中执行GUI工作)。一旦按下“Start”按钮,动作监听器(action listener)会创建SwingWorker,使得worker线程被创建。Worker线程启动后,它将执行它的construct()方法,该方法将调用doWork()(如下面的代码所示)。下面的代码例示了Example1实现的SwingWorker的子类。

  1. worker = new SwingWorker() {
  2.    public Object construct() {
  3.       return doWork();
  4.    }
  5.    public void finished() {
  6.       startButton.setEnabled(true);
  7.       interruptButton.setEnabled(false);
  8.       statusField.setText(get().toString());
  9.    }
  10. };


finished()方法在construct()方法返回后执行(即worker线程完成后)。它的任务只是简单地重新使“Start”按钮有效,同时使“Cancel”按钮无效,并将状态域显示的值设置成worker的计算结果。记住finished()方法是在事件派发线程中执行的,所以它可以安全地直接更新GUI。

例子二:从Worker线程提示用户


这个例子实现为Example1的子类。唯一的区别是,在worker线程执行了大约两秒后,它将阻塞直到用户响应一个Continue/Cancel模态对话框。如果用户选择的不是“Continue”,我们就退出doWork()循环。
这个例子演示了一个在许多worker线程中应用的惯用法:如果worker执行中到达一个不期望的状态,它将阻塞起来直到用户被提醒或用一个模态对话框从用户那里收集到了更多信息。这种做法有一点复杂,因为对话框的显示需要放到事件派发线程中,且worker线程需要被阻塞直到用户解除了该模式对话框。
我们使用SwingUtilities的invokeAndWait()方法来在事件派发线程中弹出对话框。与invokeLater()不同,invokeAndWait()会阻塞起来直到它的Runnable对象返回。在我们的例子中,Runnable对象直到对话框被解除才返回。我们创建一个内部Runnable类DoShowDialog,来完成弹出对话框。一个实例变量DoShowDialog.proceedConfirmed,被用来记录用户的选择:

  1. class DoShowDialog implements Runnable {
  2.    boolean proceedConfirmed;
  3.    public void run() {
  4.       Object[] options = {"Continue""Cancel"};
  5.          int n = JOptionPane.showOptionDialog
  6.          (Example2.this,
  7.          "Example2: Continue?",
  8.          "Example2",
  9.          JOptionPane.YES_NO_OPTION,
  10.          OptionPane.QUESTION_MESSAGE,
  11.             null,
  12.             options,
  13.             "Continue");
  14.          proceedConfirmed =
  15.             (n == JOptionPane.YES_OPTION);
  16.    }
  17. }


因为showConfirmDialog()方法弹出一个模态对话框,调用会阻塞直到用户解除该对话框。
为了显示对话框并阻塞调用线程(worker线程)直到对话框被解除,worker线程调用invokeAndWait()方法,定义一个DoShowDialog的实例:

  1. DoShowDialog doShowDialog = new DoShowDialog();
  2. try {
  3.    SwingUtilities.invokeAndWait(doShowDialog);
  4. }
  5. catch 
  6.    (java.lang.reflect.
  7.       InvocationTargetException e) {
  8.       e.printStackTrace();
  9. }


代码中捕获的InvocationTargetException是调试DoShowDialog的run()方法的残留。当invokeAndWait()方法返回后,worker线程可以读取doShowDialog.proceedConfirmed来获得用户的响应。

分享到:
评论

相关推荐

    Swing线程基础经典

    - **方法二:使用`SwingWorker`类** - Swing提供了一个`SwingWorker`类,它允许在后台线程执行计算,同时提供在EDT中更新界面的能力,通过`doInBackground`方法执行后台任务,`process`和`done`方法则在EDT中运行,...

    Swing线程基础.pdf

    Swing线程基础是Java GUI编程中的关键概念,主要涉及Swing应用程序中三种类型的线程:初始化线程、UI事件调度线程(EDT,Event Dispatch Thread)和任务线程(Worker Thread)。这些线程在Swing应用中各有其职责,以...

    Swing线程基础可用.pdf

    - 使用`SwingUtilities.invokeLater`或`invokeAndWait`:这两个方法将任务添加到EDT的队列中,确保任务在线程安全的环境中执行。 - 创建独立的线程处理长时间任务,然后通过`invokeLater`或`invokeAndWait`在EDT上...

    Java swing 完全完整实例

    这个实例集可能会涵盖上述所有知识点,甚至包括高级主题如Swing worker线程用于在后台执行任务,避免阻塞UI,以及JTable和JTree等用于显示表格和树状数据的组件。通过深入学习这些实例,开发者可以提升自己在Java ...

    java swing 多选下拉框 支持动态加载数据

    7. **Swing Worker**:为了在后台线程加载数据,避免阻塞UI,我们可以使用SwingWorker。这是一个异步执行任务并提供进度反馈的类,确保UI的流畅性。 8. **优化代码**:描述中提到“优化了网上的下拉框代码”,可能...

    swing编码艺术

    7. **Swing worker线程**:为了防止GUI线程(Event Dispatch Thread,EDT)被阻塞,Swing引入了SwingWorker,它允许在后台线程执行耗时操作,然后在EDT上更新界面,确保界面的流畅性。 8. **国际化支持**:Swing...

    swing E版

    9. **Swing worker线程**: 由于Swing组件不是线程安全的,因此为了防止UI阻塞,开发者通常会使用SwingWorker来执行耗时的任务。SwingWorker允许在后台线程中进行计算,并在完成后更新界面。 10. **JTable和JTree**:...

    Java 2图形设计 卷Ⅱ:SWING(上).rar

    10. **Swing worker线程**:Swing不鼓励在事件调度线程(EDT,Event Dispatch Thread)中执行耗时任务,因为这会阻塞用户界面的更新。SwingWorker类允许在后台线程执行计算,然后在EDT中更新UI,确保界面的响应性。 ...

    swingworker排序

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

    Jindutiao.rar_java进度条

    这段代码中,`doInBackground` 方法模拟了后台任务,`publish` 方法用于发送进度更新到 `process` 方法,该方法在事件调度线程上运行,可以安全地更新 UI。 如果你在 "Jindutiao.rar" 中的代码是基于 JavaFX 的,...

    JAVA5-6新特性列表.doc

    2. **UI的增强**:Swing组件得到了改进,如Swing Worker支持在后台线程执行任务,提升用户体验。 3. **Web Service支持的增强**:JAX-WS 2.0和JAXB 2.0提供了更强大的Web服务支持,简化了SOAP服务的开发。 4. **一...

    Java常用方法总结

    - `SwingWorker`是一个专为Swing设计的线程类,用于在后台执行耗时任务。 ```java SwingWorker, String&gt; worker = new SwingWorker, String&gt;() { @Override protected Void doInBackground() throws Exception ...

    Java 2 Java 2

    6. **Swing Worker**:为了解决GUI线程(Event Dispatch Thread,EDT)阻塞问题,SWING引入了Swing Worker,可以在后台线程执行耗时任务,保证界面的响应性。 总之,Java 2作为一个重要的Java平台版本,其强大的...

    java的gui(一)

    为了解决这个问题,我们可以创建后台线程(也称为Worker Threads)来执行这些任务,然后通过回调或发布/订阅模型更新GUI。 Graham Hamilton的“Multithreaded toolkits: A failed dream”可能讨论了多线程工具包在...

    开发J2EE应用的要领

    - **解决方案**:通过使用修订版`SwingWorker`,我们可以将数据获取逻辑放到后台线程执行,只在数据准备就绪后更新UI。 - **具体实现**: - 创建一个修订版`SwingWorker`实例,负责远程数据的获取。 - 当用户触发...

    2021-2022计算机二级等级考试试题及答案No.16624.docx

    `是在子类Worker的构造方法中调用父类Person的构造方法,而不是调用任何特定的`super()`方法(选项A和B错误),也不是语法错误(选项D错误)。 #### 题目2:Word中的文本格式化 - **知识点解析**: - 在Word中,...

    Java高级编程(JDK6版) (英文版)

    7. **改进的Swing和AWT**:JDK 6对Swing和AWT进行了优化,提供了新的组件和布局管理器,如JTabbedPane的改进,以及Swing Worker类,用于在后台线程执行耗时任务。 8. **XPath and XML Schema support**:增强了对...

    Java图像编程实例库

    8. **Swing Worker**: 当需要进行耗时的图形处理任务时,可以使用Swing Worker来避免阻塞事件调度线程,保证界面的响应性。 9. **JavaFX**: 虽然JavaFX不在Java标准库中,但它是Oracle为Java提供的一种现代的、高...

    jdk中设计模式

    10. **Decorator(装饰器模式)**:如`java.io`包和`Collections#synchronizedList(List)`,动态地给一个对象添加新的功能,避免使用继承导致类的膨胀。 11. **Façade(外观模式)**:如`java.util.logging`包,为...

Global site tag (gtag.js) - Google Analytics