javax.swing

接口
异常
java.lang.Object
  继承者 javax.swing.SwingWorker<T,V>
类型参数:
T - 此 SwingWorkerdoInBackgroundget 方法返回的结果类型
V - 用于保存此 SwingWorkerpublishprocess 方法的中间结果的类型
所有已实现的接口:
Runnable, Future<T>, RunnableFuture<T>

public abstract class SwingWorker<T,V>
     
extends Object
implements RunnableFuture<T>

在专用线程中执行长时间 GUI 交互任务的抽象类。

使用 Swing 编写多线程应用程序时,要记住两个约束条件:(有关详细信息,请参阅 How to Use Threads):

  • 不应该在事件指派线程 上运行耗时任务。否则应用程序将无响应。
  • 只能在事件指派线程 上访问 Swing 组件。

这些约束意味着需要时间密集计算操作的 GUI 应用程序至少需要以下两个线程:1) 执行长时间任务的线程; 2) 所有 GUI 相关活动的事件指派线程 (EDT)这涉及到难以实现的线程间通信。

SwingWorker 设计用于需要在后台线程中运行长时间运行任务的情况,并可在完成后或者在处理过程中向 UI 提供更新。SwingWorker 的子类必须实现 doInBackground() 方法,以执行后台计算。

工作流

SwingWorker 的生命周期中包含三个线程:

  • 当前 线程:在此线程上调用 execute() 方法。它调度 SwingWorker 以在 worker 线程上执行并立即返回。可以使用 get 方法等待 SwingWorker 完成。

  • Worker 线程:在此线程上调用 doInBackground() 方法。所有后台活动都应该在此线程上发生。要通知 PropertyChangeListeners 有关绑定 (bound) 属性的更改,请使用 firePropertyChangegetPropertyChangeSupport() 方法。默认情况下,有两个可用的绑定属性:stateprogress

  • 事件指派线程:所有与 Swing 有关的活动都在此线程上发生。SwingWorker 调用 processdone() 方法,并通知此线程的所有 PropertyChangeListener

通常,当前 线程就是事件指派线程

worker 线程上调用 doInBackground 方法之前,SwingWorker 通知所有 PropertyChangeListener 有关对 StateValue.STARTEDstate 属性更改。doInBackground 方法完成后,执行 done 方法。然后 SwingWorker 通知所有 PropertyChangeListener 有关对 StateValue.DONEstate 属性更改。

SwingWorker 被设计为只执行一次。多次执行 SwingWorker 将不会调用两次 doInBackground 方法。

示例用法

下例说明了最简单的使用范例:在后台完成某些处理,并在处理完成后更新 Swing 组件。

假定想找到“Meaning of Life”并在 JLabel 中显示结果。

final JLabel label;
class MeaningOfLifeFinder extends SwingWorker<String, Object> {
@Override
public String doInBackground() {
return findTheMeaningOfLife();
       }

@Override
protected void done() {
try { 
label.setText(get());
} catch (Exception ignore) {
           }
       }
   }
 
(new MeaningOfLifeFinder()).execute();
 

在希望处理已经在事件指派线程 上准备好的数据时,下一个例子很有用。

现在想要查找第一个 N 素数值并在 JTextArea 中显示结果。在计算过程中,想在 JProgressBar 中更新进度。最后,还要将该素数值打印到 System.out

class PrimeNumbersTask extends 
SwingWorker<List<Integer>, Integer> {
PrimeNumbersTask(JTextArea textArea, int numbersToFind) { 
//initialize 
     }

@Override
public List<Integer> doInBackground() {
while (! enough && ! isCancelled()) {
number = nextPrimeNumber();
publish(number);
setProgress(100 * numbers.size() / numbersToFind);
             }
         }
return numbers;
     }

@Override
protected void process(List<Integer> chunks) {
for (int number :chunks) {
textArea.append(number + "\n");
         }
     }
 }

JTextArea textArea = new JTextArea();
final JProgressBar progressBar = new JProgressBar(0, 100);
PrimeNumbersTask task = new PrimeNumbersTask(textArea, N);
task.addPropertyChangeListener(
new PropertyChangeListener() {
public  void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setValue((Integer)evt.getNewValue());
             }
         }
     });

task.execute();
System.out.println(task.get()); //prints all prime numbers we have got
 

因为 SwingWorker 实现了 Runnable,所以可以将 SwingWorker 提交给 Executor 执行。

从以下版本开始:
1.6

嵌套类摘要
static class SwingWorker.StateValue
          state 绑定 (bound) 属性的值。
 
构造方法摘要
SwingWorker()
          构造此 SwingWorker
 
方法摘要
 void addPropertyChangeListener(PropertyChangeListener listener)
          将 PropertyChangeListener 添加到侦听器列表。
 boolean cancel(boolean mayInterruptIfRunning)
          试图取消对此任务的执行。
protected abstract  T doInBackground()
          计算结果;如果无法计算结果,则抛出异常。
protected  void done()
          doInBackground 方法完成后,在事件指派线程 上执行此方法。
 void execute()
          调度此 SwingWorker 以便在 worker 线程上执行。
 void firePropertyChange(String propertyName, Object oldValue, Object newValue)
          向所有已注册的侦听器报告绑定属性更新。
 T get()
          如有必要,等待计算完成,然后获取其结果。
 T get(long timeout, TimeUnit unit)
          如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。
 int getProgress()
          返回 progress 绑定属性。
 PropertyChangeSupport getPropertyChangeSupport()
          返回此 SwingWorkerPropertyChangeSupport
 SwingWorker.StateValue getState()
          返回 SwingWorker 状态绑定属性。
 boolean isCancelled()
          如果在任务正常完成前将其取消,则返回 true
 boolean isDone()
          如果任务已完成,则返回 true
protected  void process(List<V> chunks)
          在事件指派线程 上异步地从 publish 方法接收数据块。
protected  void publish(V... chunks)
          将数据块发送给 process(java.util.List ) 方法。
 void removePropertyChangeListener(PropertyChangeListener listener)
          从侦听器列表中移除一个 PropertyChangeListener
 void run()
          将此 Future 设置为计算的结果,除非它已经被取消。
protected  void setProgress(int progress)
          设置 progress 绑定属性。
 
从类 java.lang.Object 继承的方法
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

构造方法详细信息

SwingWorker

public SwingWorker()
构造此 SwingWorker

方法详细信息

doInBackground

protected abstract T doInBackground()
                             throws Exception
计算结果;如果无法计算结果,则抛出异常。

注意,此方法只执行一次。

注:此方法在后台线程中执行。

返回:
计算的结果
抛出:
Exception - 如果无法计算结果

run

public final void run()
将此 Future 设置为计算的结果,除非它已经被取消。

指定者:
接口 Runnable 中的 run
指定者:
接口 RunnableFuture<T> 中的 run
另请参见:
Thread.run()

publish

protected final void publish(V... chunks)
将数据块发送给 process(java.util.List ) 方法。将从 doInBackground 方法内部使用此方法传送中间结果,以便在 process 方法内部对 事件指派线程 进行处理。

由于在事件指派进程 上异步地调用 process 方法,所以在执行 process 方法之前可以对 publish 方法进行多次调用。为了改进性能,所有这些调用都合并为一个带连接参数的调用。

例如:

publish("1");
publish("2", "3");
publish("4", "5", "6");
 
结果可能为:
process("1", "2", "3", "4", "5", "6")
 

示例用法。以下代码片断加载某一表格数据,并用它来更新 DefaultTableModel。注意,从 process 方法内部更换 tableModel 是安全的,因为该方法是在事件指派线程 上调用的。

class TableSwingWorker extends 
SwingWorker<DefaultTableModel, Object[]> {
private final DefaultTableModel tableModel;
 
public TableSwingWorker(DefaultTableModel tableModel) {
this.tableModel = tableModel;
     }
 
@Override
protected DefaultTableModel doInBackground() throws Exception {
for (Object[] row = loadData(); 
! isCancelled() && row != null; 
row = loadData()) {
publish((Object[]) row);
         }
return tableModel;
     }
 
@Override
protected void process(List<Object[]> chunks) {
for (Object[] row :chunks) {
tableModel.addRow(row);
         }
     }
 }
 

参数:
chunks - 要处理的中间结果
另请参见:
process(java.util.List )

process

protected void process(List<V> chunks)
事件指派线程 上异步地从 publish 方法接收数据块。

有关详细信息,请参阅 publish(V...) 方法。

参数:
chunks - 要处理的中间结果
另请参见:
publish(V...)

done

protected void done()
doInBackground 方法完成后,在 事件指派线程 上执行此方法。默认实现不执行任何操作。子类可以重写此方法,以在 事件指派线程 上执行完成操作。注意,可以查询此方法实现内部的状态,以确定此任务的结果或者是否已经取消了此任务。

另请参见:
doInBackground(), isCancelled(), get()

setProgress

protected final void setProgress(int progress)
设置 progress 绑定属性。该值应该在 0 到 100 之间。

由于在事件指派线程 上异步地通知 PropertyChangeListener,所以可以在调用任意 PropertyChangeListener 之前对 setProgress 方法进行多次调用。为了改进性能,所有这些调用都合并为一个只带最后一个调用参数的调用。

例如,以下调用:

setProgress(1);
setProgress(2);
setProgress(3);
 
结果可能为带有值 3 的单个 PropertyChangeListener 通知。

参数:
progress - 要设置的进度值
抛出:
IllegalArgumentException - 如果值不是从 0 到 100

getProgress

public final int getProgress()
返回 progress 绑定属性。

返回:
progress 绑定属性。

execute

public final void execute()
调度此 SwingWorker 以便在 worker 线程上执行。存在许多可用的 worker 线程。如果所有 worker 线程都忙于处理其他 SwingWorker,则此 SwingWorker 将被放入等待队列。

注:SwingWorker 被设计为只执行一次。多次执行 SwingWorker 将不会调用两次 doInBackground 方法。


cancel

public final boolean cancel(boolean mayInterruptIfRunning)
试图取消对此任务的执行。如果任务已完成、或已取消,或者由于某些其他原因而无法取消,则此尝试将失败。当调用 cancel 时,如果调用成功,而此任务尚未启动,则此任务将永不运行。如果任务已经启动,则 mayInterruptIfRunning 参数确定是否应该以试图停止任务的方式来中断执行此任务的线程。

此方法返回后,对 Future.isDone() 的后续调用将始终返回 true。如果此方法返回 true,则对 Future.isCancelled() 的后续调用将始终返回 true

指定者:
接口 Future<T> 中的 cancel
参数:
mayInterruptIfRunning - 如果应该中断执行此任务的线程,则为 true;否则允许正在运行的任务运行完成
返回:
如果无法取消任务,则返回 false,这通常是由于它已经正常完成;否则返回 true

isCancelled

public final boolean isCancelled()
如果在任务正常完成前将其取消,则返回 true

指定者:
接口 Future<T> 中的 isCancelled
返回:
如果任务完成前将其取消,则返回 true

isDone

public final boolean isDone()
如果任务已完成,则返回 true。 可能由于正常终止、异常或取消而完成,在所有这些情况中,此方法都将返回 true

指定者:
接口 Future<T> 中的 isDone
返回:
如果任务已完成,则返回 true

get

public final T get()
            throws InterruptedException,
                   ExecutionException
如有必要,等待计算完成,然后获取其结果。

注:在此 SwingWorker 完成之前,在事件指派线程 上调用 get 将阻塞所有 事件(包括 repaint)的处理。

想在事件指派线程 上阻塞 SwingWorker 时,建议使用 模式对话框(modal dialog)

例如:

class SwingWorkerCompletionWaiter extends PropertyChangeListener {
private JDialog dialog;
 
public SwingWorkerCompletionWaiter(JDialog dialog) {
this.dialog = dialog;
     }
 
public void propertyChange(PropertyChangeEvent event) {
if ("state".equals(event.getPropertyName())
&& SwingWorker.StateValue.DONE == event.getNewValue()) {
dialog.setVisible(false);
dialog.dispose();
         }
     }
 }
JDialog dialog = new JDialog(owner, true);
swingWorker.addPropertyChangeListener(
new SwingWorkerCompletionWaiter(dialog));
swingWorker.execute();
//the dialog will be visible until the SwingWorker is done
dialog.setVisible(true); 
 

指定者:
接口 Future<T> 中的 get
返回:
计算的结果
抛出:
InterruptedException - 如果当前的线程在等待时被中断
ExecutionException - 如果计算抛出异常

get

public final T get(long timeout,
                   TimeUnit unit)
            throws InterruptedException,
                   ExecutionException,
                   TimeoutException
如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。

有关更多信息,请参阅 get()

指定者:
接口 Future<T> 中的 get
参数:
timeout - 等待的最大时间
unit - timeout 参数的时间单位
返回:
计算的结果
抛出:
InterruptedException - 如果当前的线程在等待时被中断
ExecutionException - 如果计算抛出异常
TimeoutException - 如果等待超时

addPropertyChangeListener

public final void addPropertyChangeListener(PropertyChangeListener listener)
PropertyChangeListener 添加到侦听器列表。该侦听器是为所有属性注册的。同一侦听器对象可以被添加多次,并且它们被调用的次数将与添加它们的次数相同。如果 listenernull,则不抛出任何异常并且不执行任何操作。

注:此方法只是一个便捷包装器。所有工作都委托给 getPropertyChangeSupport() 返回的 PropertyChangeSupport

参数:
listener - 要添加的 PropertyChangeListener

removePropertyChangeListener

public final void removePropertyChangeListener(PropertyChangeListener listener)
从侦听器列表中移除一个 PropertyChangeListener。此方法移除一个为所有属性注册的 PropertyChangeListener。如果将 listener 多次添加到同一事件源,则在被移除之后,它获得的通知将减少一次。如果 listenernull,或者没有添加过,则不抛出任何异常并且不执行任何操作。

注:此方法只是一个便捷包装器。所有工作都委托给 getPropertyChangeSupport() 返回的 PropertyChangeSupport

参数:
listener - 要移除的 PropertyChangeListener

firePropertyChange

public final void firePropertyChange(String propertyName,
                                     Object oldValue,
                                     Object newValue)
向所有已注册的侦听器报告绑定属性更新。如果 oldnew 相等并且为非 null,则不触发任何事件。

SwingWorker 将成为所有已生成事件的源。

事件指派线程 之外调用此方法时,则在事件指派线程 上异步地通知 PropertyChangeListener

注:此方法只是一个便捷包装器。所有工作都委托给 getPropertyChangeSupport() 返回的 PropertyChangeSupport

参数:
propertyName - 已更改属性的编程名称
oldValue - 属性的旧值
newValue - 属性的新值

getPropertyChangeSupport

public final PropertyChangeSupport getPropertyChangeSupport()
返回此 SwingWorkerPropertyChangeSupport。当需要灵活地访问绑定属性支持时,使用此方法。

SwingWorker 将成为所有已生成事件的源。

注:如果在事件指派线程 之外调用 firePropertyChangefireIndexedPropertyChange,则返回的 PropertyChangeSupport事件指派线程 上异步地通知所有 PropertyChangeListener

返回:
SwingWorkerPropertyChangeSupport

getState

public final SwingWorker.StateValue getState()
返回 SwingWorker 状态绑定属性。

返回:
当前状态