使用isCancelled()时SwingWorker不停止

时间:2014-08-16 23:56:14

标签: java multithreading user-interface swingworker

我创造了一个有3个按钮的gui;连接,断开,中止。它们共享一个共同的SwingWorker线程,但由于某种原因线程永远不会完成,除非我点击中止按钮。

我还没有为断开按钮编写任何代码,但即便如此,这与我的问题无关。但是,为什么线程不会完成并打印Done语句?我的猜测是它被卡在this.isCancelled()循环中,但即使我这样做:

while(!this.isCancelled() || !this.isDone()) { // run loop }

它从不打印Done

一旦我点击中止按钮,线程就会完成。我正在使用isCancelled()循环方法,因为这似乎是取消SwingWorker的推荐方法。

我只发布下面的相关代码,因为有数百行。

   private class ButtonListener implements ActionListener {

      private MyWorker worker;

      @Override
      public void actionPerformed(ActionEvent e) {

         if(e.getSource().equals(connectButton)) { 

            worker = new MyWorker(activityTextArea);
            worker.execute();
            activityTextArea.append("Connect Button Pressed"); 
         }

         if(e.getSource().equals(disconnectButton)) { 

            activityTextArea.append("Disconnect Button Pressed"); 
         }

         if(e.getSource().equals(abortButton)) { 

            worker.cancel(true);
            activityTextArea.append("Abort Button Pressed"); 
         }
      }
   }

这是SwingWorker:

public class MyWorker extends SwingWorker<Void, Void> {

   private JTextArea textArea;

   MyWorker(JTextArea textArea) {
      this.textArea = textArea;
   }

   private void startWorker() {

      ProcessBuilder pb = new ProcessBuilder();
      pb.command("aCmd");

      Process p;

      try {
         p = pb.start();

         BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));

         String line;

         while((line = br.readLine()) != null) {
            textArea.append(line + "\n");
         }
         br.close();
      } catch (IOException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
   }

   @Override
   protected Void doInBackground() throws Exception {
      // TODO Auto-generated method stub
      boolean isFirstTime = true;

      while(!this.isCancelled()) {

         if(this.isDone()) { break; }

         Thread.sleep(3000);

         if(isFirstTime) {
            startWorker();
            isFirstTime = false;
         } 
      }

      return null;
   }

   @Override
   protected void done() {
      textArea.append("Done\n");
   }
}

1 个答案:

答案 0 :(得分:1)

这里的问题是在MyWorker.doInBackground()方法的实现中,isDone()用于控制循环的执行。这可能不是你打算如何使用doInBackground()。

要真正了解最新情况,我们需要考虑isDone()的行为和SwingWorker的生命周期。

对于这些场景,isDone()方法返回true:

  • 完成执行后()
  • 工人已被取消
  • 由于某些异常导致异常终止。

SwingWorker LifeCycle

  • PENDING :从构造对象到调用doInBackground之前的状态。
  • 已启动:调用doInBackground()前不久的时间段内调用done()前的状态。在进入此状态之前,SwingWorker会分配一个工作线程,并且此工作线程执行doInBackground()。
    • 在此状态下执行doInBackground()时,工作人员可以使用publish(..)发送中间结果,然后通过事件调度线程(EDT)获取process(..)
  • DONE :在done()在事件调度线程(EDT)中执行完毕后,转换到此状态。这是对象存在的剩余部分的最终状态。

了解上述工作流程非常重要,以便编写一个高性能且无故障的GUI。

根据上面的讨论,您可能希望将if(this.isDone()) { break; }替换为反映预期处理完成情况的内容。

这里的另一个问题是你在由doInBackground()调用的方法中进行与swing相关的活动。 textArea.append(line + "\n");应该发生在Event Dispatch Thread中执行的process(..)方法中,如下所示:

public class MyWorker extends SwingWorker<Void, String> {

    @Override
    protected void process(List<String> chunks) {
        for (String line : chunks) {
            textArea.append(line + "\n"); // display intermediate results from publish()
        }
    }

    private void startWorker() throws IOException {

        ProcessBuilder pb = new ProcessBuilder("aCmd");
        BufferedReader br = null;

        try {
            Process p = pb.start();
            br = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String line;
            while ((line = br.readLine()) != null) {
                publish(line); // publish a chunk of result to process(..)
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) br.close();
        }
    }

参考:http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingWorker.html