模块 java.desktop

类 SwingWorker<T,V>

java.lang.Object
javax.swing.SwingWorker<T,V>
类型参数:
T - 此 SwingWorker's doInBackgroundget 方法返回的结果类型
V - 用于通过此 SwingWorker's publishprocess 方法执行中间结果的类型
所有已实现的接口:
Runnable , Future<T> , RunnableFuture<T>

public abstract class SwingWorker<T,V> extends Object implements RunnableFuture <T>
一个抽象类,用于在后台线程中执行冗长的 GUI 交互任务。可以使用多个后台线程来执行此类任务。然而,为任何特定的 SwingWorker 选择线程的确切策略是未指定的,不应依赖。

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

  • 耗时的任务不应该运行在事件派发线程.否则应用程序将变得无响应。
  • 应访问 Swing 组件事件派发线程仅有的。

这些限制意味着具有时间密集型计算的 GUI 应用程序至少需要两个线程:1) 一个线程来执行冗长的任务和 2)事件派发线程(EDT) 用于所有与 GUI 相关的活动。这涉及线程间通信,实现起来可能很棘手。

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

工作流程

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

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

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

  • 事件派发线程:所有与 Swing 相关的活动都发生在该线程上。 SwingWorker 调用 process done() 方法并通知此线程上的任何 PropertyChangeListeners

通常,当前的线程是事件派发线程.

在调用 doInBackground 方法之前工人线程,SwingWorker 通知任何 PropertyChangeListeners 关于 state 属性更改为 StateValue.STARTEDdoInBackground 方法完成后,执行 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) {
     //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
  • 构造方法详细信息

    • SwingWorker

      public SwingWorker()
      构造这个 SwingWorker
  • 方法详情

    • 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 是异步通知的事件派发线程在调用任何 PropertyChangeListeners 之前,可能会多次调用 setProgress 方法。出于性能目的,所有这些调用都合并为一个仅包含最后一个调用参数的调用。

      例如,以下调用:

       setProgress(1);
       setProgress(2);
       setProgress(3);
       
      可能会产生一个值为 3PropertyChangeListener 通知。
      参数:
      progress - 要设置的进度值
      抛出:
      IllegalArgumentException - 值不在 0 到 100 之间
    • getProgress

      public final int getProgress()
      返回 progress 绑定属性。
      返回:
      进度绑定属性。
    • execute

      public final void execute()
      安排此 SwingWorker 执行工人线。有许多工人线程可用。在所有情况下工人threads are busy handling other SwingWorkers this SwingWorker is placed in a waiting queue.

      注意: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事件派发线程积木all事件,包括重绘,从被处理直到这个 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();
       //the dialog will be visible until the SwingWorker is done
       dialog.setVisible(true);
       
      指定者:
      get 在接口 Future<T>
      返回:
      计算结果
      抛出:
      CancellationException - 如果计算被取消
      InterruptedException - 如果当前线程在等待时被中断
      ExecutionException - 如果计算抛出异常
    • get

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

      请参阅 get() 了解更多详情。

      指定者:
      get 在接口 Future<T>
      参数:
      timeout - 最长时间等待
      unit - 超时参数的时间单位
      返回:
      计算结果
      抛出:
      CancellationException - 如果计算被取消
      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 相等且非空,则不会触发任何事件。

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

      当被叫停事件派发线程PropertyChangeListeners 被异步通知事件派发线程.

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

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

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

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

      注意:返回的 PropertyChangeSupport 异步通知任何 PropertyChangeListener s事件派发线程如果 firePropertyChangefireIndexedPropertyChange 被取消事件派发线程.

      返回:
      PropertyChangeSupport 对于这个 SwingWorker
    • getState

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