Module java.desktop
Package javax.swing

Class SwingWorker<T,V>

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交互任务的抽象类。可以使用多个后台线程来执行这些任务。但是,选择任何特定SwingWorker的线程的确切策略是未指定的,不应依赖于它。

在使用Swing编写多线程应用程序时,有两个约束需要牢记:(有关更多详细信息,请参阅Swing中的并发性):

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

这些约束意味着具有耗时计算需求的GUI应用程序至少需要两个线程:1) 用于执行耗时任务的线程,2) 用于所有GUI相关活动的事件分派线程(EDT)。这涉及跨线程通信,可能难以实现。

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

工作流程

SwingWorker的生命周期涉及三个线程:

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

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

  • 事件分派线程:所有与Swing相关的活动发生在此线程上。SwingWorker调用processdone方法,并在此线程上通知任何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
  • Constructor Details

    • SwingWorker

      public SwingWorker()
      构造此SwingWorker
  • Method Details

    • doInBackground

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

      请注意,此方法仅执行一次。

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

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

      public final void run()
      将此Future设置为计算的结果,除非已取消。
      指定者:
      run 在接口 Runnable
      指定者:
      run 在接口 RunnableFuture<T>
    • publish

      @SafeVarargs protected final void publish(V... chunks)
      将数据块发送到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

      protected void process(List<V> chunks)
      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()

      指定者:
      cancel 在接口 Future<T>
      参数:
      mayInterruptIfRunning - 如果应中断执行此任务的线程(如果实现已知),则为true;否则,允许正在进行的任务完成
      返回:
      如果任务无法取消,则返回false,通常是因为任务已经完成;否则返回true。如果两个或更多线程导致任务被取消,则至少有一个返回true。实现可能提供更强的保证。
    • isCancelled

      public final boolean isCancelled()
      如果此任务在正常完成之前被取消,则返回true
      指定者:
      isCancelled 在接口 Future<T>
      返回:
      如果此任务在完成之前被取消,则返回true
    • isDone

      public final boolean isDone()
      如果此任务已完成,则返回true。完成可能是由于正常终止、异常或取消 - 在所有这些情况下,此方法将返回true
      指定者:
      isDone 在接口 Future<T>
      返回:
      如果此任务已完成,则返回true
    • get

      public final T get() throws InterruptedException, ExecutionException
      必要时等待计算完成,然后检索其结果。

      注意:在事件分派线程上调用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

      public final void addPropertyChangeListener(PropertyChangeListener listener)
      向监听器列表中添加一个PropertyChangeListener。该监听器将被注册为所有属性的监听器。同一个监听器对象可以被添加多次,并且每次添加都会被调用。如果listenernull,则不会抛出异常,也不会执行任何操作。

      注意:这只是一个方便的包装器。所有工作都委托给PropertyChangeSupportgetPropertyChangeSupport()方法。

      参数:
      listener - 要添加的PropertyChangeListener
    • removePropertyChangeListener

      public final void removePropertyChangeListener(PropertyChangeListener listener)
      从监听器列表中移除一个PropertyChangeListener。这将移除为所有属性注册的PropertyChangeListener。如果同一个事件源多次添加了listener,则在移除后将少通知一次。如果listenernull,或者从未添加过,则不会抛出异常,也不会执行任何操作。

      注意:这只是一个方便的包装器。所有工作都委托给PropertyChangeSupportgetPropertyChangeSupport()方法。

      参数:
      listener - 要移除的PropertyChangeListener
    • firePropertyChange

      public final void firePropertyChange(String propertyName, Object oldValue, Object newValue)
      向所有注册的监听器报告一个绑定属性更新。如果oldnew相等且非空,则不会触发事件。

      这个SwingWorker将是任何生成事件的源。

      事件分发线程之外调用时,PropertyChangeListeners将在事件分发线程上异步通知。

      注意:这只是一个方便的包装器。所有工作都委托给PropertyChangeSupportgetPropertyChangeSupport()方法。

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

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

      这个SwingWorker将是任何生成事件的源。

      注意:返回的PropertyChangeSupport会在调用firePropertyChangefireIndexedPropertyChange时,如果在事件分发线程之外调用,则会在事件分发线程上异步通知任何PropertyChangeListener

      返回值:
      SwingWorkerPropertyChangeSupport
    • getState

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