- 类型参数:
-
T
- 由此SwingWorker
的doInBackground
和get
方法返回的结果类型 -
V
- 由此SwingWorker
的publish
和process
方法用于传递中间结果的类型
- 所有实现的接口:
-
Runnable
,Future<T>
,RunnableFuture<T>
SwingWorker
的线程的确切策略是未指定的,不应依赖于它。
在使用Swing编写多线程应用程序时,有两个约束需要牢记:(有关更多详细信息,请参阅Swing中的并发性):
- 耗时任务不应在事件分派线程上运行。否则应用程序会变得无响应。
- Swing组件只能在事件分派线程上访问。
这些约束意味着具有耗时计算需求的GUI应用程序至少需要两个线程:1) 用于执行耗时任务的线程,2) 用于所有GUI相关活动的事件分派线程(EDT)。这涉及跨线程通信,可能难以实现。
SwingWorker
设计用于需要在后台线程中运行长时间运行任务并在完成时或处理过程中向UI提供更新的情况。 SwingWorker
的子类必须实现doInBackground()
方法来执行后台计算。
工作流程
SwingWorker
的生命周期涉及三个线程:
-
当前线程:在此线程上调用
execute()
方法。它安排SwingWorker
在工作线程上执行并立即返回。可以使用get
方法等待SwingWorker
完成。 -
工作线程:在此线程上调用
doInBackground()
方法。这是所有后台活动应发生的地方。要通知PropertyChangeListeners
有关绑定属性更改,请使用firePropertyChange
和getPropertyChangeSupport()
方法。默认情况下,有两个可用的绑定属性:state
和progress
。 -
事件分派线程:所有与Swing相关的活动发生在此线程上。
SwingWorker
调用process
和done
方法,并在此线程上通知任何PropertyChangeListeners
。
通常,当前线程是事件分派线程。
在工作线程上调用doInBackground
方法之前,SwingWorker
会通知任何PropertyChangeListeners
有关state
属性更改为StateValue.STARTED
。在doInBackground
方法完成后,将执行done
方法。然后,SwingWorker
会通知任何PropertyChangeListeners
有关state
属性更改为StateValue.DONE
。
SwingWorker
仅设计为执行一次。多次执行SwingWorker
不会导致调用doInBackground
方法两次。
示例用法
以下示例说明了最简单的用法。在后台进行一些处理,完成后更新Swing组件。
假设我们想找到"生命的意义"并在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) { //初始化 }@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()); //打印我们得到的所有质数
因为SwingWorker
实现了Runnable
,所以可以将SwingWorker
提交给Executor
以执行。
- 自1.6版本起:
- 1.6
-
Nested Class Summary
Nested classes/interfaces declared in interface java.util.concurrent.Future
Future.State
-
Constructor Summary
-
Method Summary
Modifier and TypeMethodDescriptionfinal void
向侦听器列表添加PropertyChangeListener
。final boolean
cancel
(boolean mayInterruptIfRunning) 尝试取消执行此任务。protected abstract T
计算结果,如果无法执行则抛出异常。protected void
done()
在doInBackground
方法完成后在事件分派线程上执行。final void
execute()
将此SwingWorker
安排在工作线程上执行。final void
firePropertyChange
(String propertyName, Object oldValue, Object newValue) 向任何已注册的侦听器报告绑定属性更新。final T
get()
如有必要等待计算完成,然后检索其结果。final T
如有必要等待最多指定时间以完成计算,然后检索其结果(如果可用)。final int
返回progress
绑定属性。final PropertyChangeSupport
返回此SwingWorker
的PropertyChangeSupport
。final SwingWorker.StateValue
getState()
返回SwingWorker
状态绑定属性。final boolean
如果此任务在正常完成之前被取消,则返回true
。final boolean
isDone()
如果此任务已完成,则返回true
。protected void
在事件分派线程上异步接收来自publish
方法的数据块。protected final void
将数据块发送到process(java.util.List<V>)
方法。final void
从侦听器列表中删除PropertyChangeListener
。final void
run()
将此Future
设置为计算的结果,除非已取消。protected final void
setProgress
(int progress) 设置progress
绑定属性。Methods declared in class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
Methods declared in interface java.util.concurrent.Future
exceptionNow, resultNow, state
-
Constructor Details
-
SwingWorker
public SwingWorker()构造此SwingWorker
。
-
-
Method Details
-
doInBackground
计算结果,如果无法执行则抛出异常。请注意,此方法仅执行一次。
注意:此方法在后台线程中执行。
- 返回:
- 计算的结果
- 抛出:
-
Exception
- 如果无法计算结果
-
run
public final void run()将此Future
设置为计算的结果,除非已取消。- 指定者:
-
run
在接口Runnable
中 - 指定者:
-
run
在接口RunnableFuture<T>
中
-
publish
将数据块发送到process(java.util.List<V>)
方法。此方法应从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
从publish
方法异步地在事件分派线程上接收数据块。请参阅
publish(V...)
方法以获取更多详细信息。- 参数:
-
chunks
- 要处理的中间结果 - 参见:
-
done
protected void done()在doInBackground
方法完成后在事件分派线程上执行。默认实现不执行任何操作。子类可以重写此方法,在事件分派线程上执行完成操作。请注意,您可以在此方法的实现内部查询状态,以确定此任务的结果或此任务是否已被取消。- 参见:
-
setProgress
protected final void setProgress(int progress) 设置progress
绑定属性。该值应为0到100。由于
PropertyChangeListener
在事件分派线程上异步通知,可能会在调用setProgress
方法之前发生多次调用setProgress
方法。出于性能考虑,所有这些调用都会合并为一个带有最后一个调用参数的调用。例如,以下调用:
setProgress(1); setProgress(2); setProgress(3);
可能导致单个PropertyChangeListener
通知,值为3
。- 参数:
-
progress
- 要设置的进度值 - 抛出:
-
IllegalArgumentException
- 如果值不在0到100之间
-
getProgress
public final int getProgress()返回progress
绑定属性。- 返回:
- 进度绑定属性。
-
execute
public final void execute()将此SwingWorker
安排在工作线程上执行。有许多可用的工作线程。如果所有工作线程都忙于处理其他SwingWorker
,则将此SwingWorker
放入等待队列中。注意:
SwingWorker
仅设计为执行一次。多次执行SwingWorker
不会导致调用doInBackground
方法两次。 -
cancel
public final boolean cancel(boolean mayInterruptIfRunning) 尝试取消此任务的执行。如果任务已经完成或取消,或者由于其他原因无法取消,则此方法不起作用。否则,如果在调用cancel
时任务尚未启动,则此任务不应运行。如果任务已经启动,则mayInterruptIfRunning
参数确定是否应中断执行此任务的线程(如果实现已知)以尝试停止任务。此方法的返回值并不一定表示任务现在已取消;请使用
Future.isCancelled()
。 -
isCancelled
public final boolean isCancelled()如果此任务在正常完成之前被取消,则返回true
。- 指定者:
-
isCancelled
在接口Future<T>
中 - 返回:
-
如果此任务在完成之前被取消,则返回
true
-
isDone
public final boolean isDone()如果此任务已完成,则返回true
。完成可能是由于正常终止、异常或取消 - 在所有这些情况下,此方法将返回true
。 -
get
必要时等待计算完成,然后检索其结果。注意:在事件分派线程上调用
get
会阻塞所有事件,包括重绘事件,直到此SwingWorker
完成。当您希望
SwingWorker
在事件分派线程上阻塞时,我们建议您使用模态对话框。例如:
class SwingWorkerCompletionWaiter implements 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(); //对话框将一直可见,直到SwingWorker完成 dialog.setVisible(true);
- 指定者:
-
get
在接口Future<T>
中 - 返回:
- 计算结果
- 抛出:
-
CancellationException
- 如果计算被取消 -
InterruptedException
- 如果当前线程在等待时被中断 -
ExecutionException
- 如果计算引发异常
-
get
public final T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException - 指定者:
-
get
在接口Future<T>
中 - 参数:
-
timeout
- 最长等待时间 -
unit
- 超时参数的时间单位 - 返回值:
- 计算结果
- 抛出:
-
CancellationException
- 如果计算被取消 -
InterruptedException
- 如果当前线程在等待时被中断 -
ExecutionException
- 如果计算抛出异常 -
TimeoutException
- 如果等待超时
-
addPropertyChangeListener
向监听器列表中添加一个PropertyChangeListener
。该监听器将被注册为所有属性的监听器。同一个监听器对象可以被添加多次,并且每次添加都会被调用。如果listener
为null
,则不会抛出异常,也不会执行任何操作。注意:这只是一个方便的包装器。所有工作都委托给
PropertyChangeSupport
的getPropertyChangeSupport()
方法。- 参数:
-
listener
- 要添加的PropertyChangeListener
-
removePropertyChangeListener
从监听器列表中移除一个PropertyChangeListener
。这将移除为所有属性注册的PropertyChangeListener
。如果同一个事件源多次添加了listener
,则在移除后将少通知一次。如果listener
为null
,或者从未添加过,则不会抛出异常,也不会执行任何操作。注意:这只是一个方便的包装器。所有工作都委托给
PropertyChangeSupport
的getPropertyChangeSupport()
方法。- 参数:
-
listener
- 要移除的PropertyChangeListener
-
firePropertyChange
向所有注册的监听器报告一个绑定属性更新。如果old
和new
相等且非空,则不会触发事件。这个
SwingWorker
将是任何生成事件的源。在事件分发线程之外调用时,
PropertyChangeListeners
将在事件分发线程上异步通知。注意:这只是一个方便的包装器。所有工作都委托给
PropertyChangeSupport
的getPropertyChangeSupport()
方法。- 参数:
-
propertyName
- 已更改的属性的程序名称 -
oldValue
- 属性的旧值 -
newValue
- 属性的新值
-
getPropertyChangeSupport
返回此SwingWorker
的PropertyChangeSupport
。当需要灵活访问绑定属性支持时使用此方法。这个
SwingWorker
将是任何生成事件的源。注意:返回的
PropertyChangeSupport
会在调用firePropertyChange
或fireIndexedPropertyChange
时,如果在事件分发线程之外调用,则会在事件分发线程上异步通知任何PropertyChangeListener
。- 返回值:
-
此
SwingWorker
的PropertyChangeSupport
-
getState
返回SwingWorker
状态绑定属性。- 返回值:
- 当前状态
-