2013-03-01 148 views
1

好的,所以我一直在玩SwingWorker,并得到了一些简化的代码来更新gui,但我无法弄清楚如何让线程正常终止当它完成时。目前,它只是通过停止选项终止。我将如何设置它,以便在完成其过程时正确终止线程?目前,在return null;之后,它进入包装行并挂起。Java SwingWorker没有终止任务完成

我的代码如下:

package concurrency; 

import java.util.List; 
import java.util.Random; 
import java.awt.event.ActionListener; 
import java.awt.event.ActionEvent; 
import java.awt.GridBagLayout; 
import java.awt.GridBagConstraints; 
import java.awt.Insets; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JOptionPane; 
import javax.swing.JProgressBar; 
import javax.swing.SwingUtilities; 
import javax.swing.SwingWorker; 

public class PBTest extends JFrame implements ActionListener { 
    private final GridBagConstraints constraints; 
    private final JProgressBar pb, pbF; 
    private final JButton theButton; 
    private PBTask pbTask; 

    private JProgressBar makePB() { 
     JProgressBar p = new JProgressBar(0,100); 
     p.setValue(0); 
     p.setStringPainted(true); 
     getContentPane().add(p, constraints); 
     return p;  
    } 

    public PBTest() { 
     super("PBTest"); 
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     //Make text boxes 
     getContentPane().setLayout(new GridBagLayout()); 
     constraints = new GridBagConstraints(); 
     constraints.insets = new Insets(3, 10, 3, 10); 

     pb = makePB(); 
     pbF = makePB(); 

     //Make buttons 
     theButton = new JButton("Start"); 
     theButton.setActionCommand("Start"); 
     theButton.addActionListener(this); 
     getContentPane().add(theButton, constraints); 

     //Display the window. 
     pack(); 
     setVisible(true); 
    } 

    private static class UpdatePB { 
     private final int pb1, pb2; 
     UpdatePB(int pb1s, int pb2s) { 
      this.pb1 = pb1s; 
      this.pb2 = pb2s; 
     } 
    } 

    private class PBTask extends SwingWorker<Void, UpdatePB> { 
     @Override 
     protected Void doInBackground() { 
      int prog1 = 0; 
      int prog2 = 0; 
      Random random = new Random(); 

      while (prog2 < 100) { 
       if(prog1 >= 100) { 
        prog1 = 0; 
       } 
       //Sleep for up to one second. 
       try { 
        Thread.sleep(random.nextInt(1000)); 
       } catch (InterruptedException ignore) {} 
       //Make random progress. 
       prog1 += random.nextInt(10); 
       prog2 += random.nextInt(5); 
       publish(new UpdatePB(prog1, prog2)); 
      } 
      return null; 
     } 

     @Override 
     protected void process(List<UpdatePB> pairs) { 
      UpdatePB pair = pairs.get(pairs.size() - 1); 
       pb.setValue(pair.pb1); 
       pbF.setValue(pair.pb2); 
     } 
    } 

    public void actionPerformed(ActionEvent e) { 
     if ("Start" == e.getActionCommand() && pbTask == null) { 
      theButton.setText("Stop"); 
      theButton.setActionCommand("Stop"); 
      (pbTask = new PBTask()).execute(); 
     } else if ("Stop" == e.getActionCommand()) { 
      theButton.setText("Start"); 
      theButton.setActionCommand("Start"); 
      pbTask.cancel(true); 
      pbTask = null; 
     } else { 
      alertMsg("Thread still running."); 
     } 

    } 

    static void alertMsg(String theAlert) { 
     JOptionPane.showMessageDialog(null, theAlert); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
      public void run() { 
       new PBTest(); 
      } 
     }); 
    } 
} 

注:这是基本的Java教程‘脚蹼’的例子的改变......我没有那么多程序员的代码黑客(/伤心面对/,大声笑),此刻,所以我有点不知道下一步该去哪里。

无论如何,该代码的工作原理,直到它完成。我尝试添加done()方法,但它永远不会尝试运行它,它总是只进入包行(当通过调试器时)并挂起。我应该返回一个非零值的东西吗?

在此先感谢您的帮助!

+1

使用''==操作符不要拿'String's。使用['String.equals()'](http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#equals(java.lang.Object))方法。 – Crozin 2013-03-01 20:06:48

+0

感谢您的提示。这就是它在Java教程网站的脚本示例中的设置。我用==比较字符串的方法遇到了麻烦,所以希望这有助于这一点。谢谢!现在,如果我能弄清楚如何让线程正常完成而不用强制解决。 – DGolberg 2013-03-01 20:11:56

回答

3

我不确定你想要达到的目标。 你的例子工作正常。工作线程运行到结束。 如果你想等到它结束做某事,你必须在你的代码中的某处调用方法pbTask.get()。否则,它会静静地完成,而不会影响你的任何UI组件。

考虑对您的方法进行以下更改,以了解它现在的行为。请注意,UI会因用户等待线程完成而冻结,但只有在WorkerThread完成时才会在日志中输出“DONE”。

public void actionPerformed(ActionEvent e) { 
    if ("Start" == e.getActionCommand() && pbTask == null) { 
     theButton.setText("Stop"); 
     theButton.setActionCommand("Stop"); 
     (pbTask = new PBTask()).execute(); 
    } else if ("Stop" == e.getActionCommand()) { 
     theButton.setText("Start"); 
     theButton.setActionCommand("Start"); 
     pbTask.cancel(true); 
     pbTask = null; 
    } else { 
     alertMsg("Thread still running."); 
    } 
    try { 
     pbTask.get(); 
    } catch (InterruptedException e1) { 
     // TODO Auto-generated catch block 
     e1.printStackTrace(); 
    } catch (ExecutionException e1) { 
     // TODO Auto-generated catch block 
     e1.printStackTrace(); 
    } 
    System.out.println("DONE"); 
} 

此更改仅用于说明差异。为了编写实际的代码,我们需要更多地了解你想要达到的目标。


如果我的超感技能没问题,那么您可能想要将按钮翻回“开始”。为了做到这一点,你需要重写完成()方法在工:

private class PBTask extends SwingWorker<Void, UpdatePB> { 
    @Override 
    protected Void doInBackground() { 
     int prog1 = 0; 
     int prog2 = 0; 
     Random random = new Random(); 

     while (prog2 < 100) { 
      if(prog1 >= 100) { 
       prog1 = 0; 
      } 
      //Sleep for up to one second. 
      try { 
       Thread.sleep(random.nextInt(100)); 
      } catch (InterruptedException ignore) {} 
      //Make random progress. 
      prog1 += random.nextInt(10); 
      prog2 += random.nextInt(5); 
      publish(new UpdatePB(prog1, prog2)); 
     } 
     return null; 
    } 

    @Override 
    protected void process(List<UpdatePB> pairs) { 
     UpdatePB pair = pairs.get(pairs.size() - 1); 
      pb.setValue(pair.pb1); 
      pbF.setValue(pair.pb2); 
    } 

    @Override 
    protected void done() { 
     super.done(); 
     theButton.setText("Start"); 
     theButton.setActionCommand("Start"); 
    } 
} 
+0

嗯,我必须看看那些持有UI部分的摆动工具的残余部分,然后...我现在有点笨......我想我需要的只是把'pbTask = null;'放到'返回null;'。您的代码确实可以确认它已完成,但是......谢谢! – DGolberg 2013-03-01 20:27:45

+0

显然这是一个经典的案例,我认为它比实际涉及的更多,哈哈! – DGolberg 2013-03-01 20:30:02

+0

你的编辑正是我的想法......你的超感官技能已经死了!大声笑。我可以在线程结束时重置按钮...但这看起来不正确。你提到的方法似乎对我更“正式”。 – DGolberg 2013-03-01 20:39:04

4

例如

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.*; 
import java.util.ArrayList; 

public class SwingWorkerExample extends JFrame implements ActionListener { 

    private static final long serialVersionUID = 1L; 
    private final JButton startButton, stopButton; 
    private JScrollPane scrollPane = new JScrollPane(); 
    private JList listBox = null; 
    private DefaultListModel listModel = new DefaultListModel(); 
    private final JProgressBar progressBar; 
    private mySwingWorker swingWorker; 

    public SwingWorkerExample() { 
     super("SwingWorkerExample"); 
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     getContentPane().setLayout(new GridLayout(2, 2)); 
     startButton = makeButton("Start"); 
     stopButton = makeButton("Stop"); 
     stopButton.setEnabled(false); 
     progressBar = makeProgressBar(0, 99); 
     listBox = new JList(listModel); 
     scrollPane.setViewportView(listBox); 
     getContentPane().add(scrollPane); 
     //Display the window. 
     pack(); 
     setVisible(true); 
    } 
//Class SwingWorker<T,V> T - the result type returned by this SwingWorker's doInBackground 
//and get methods V - the type used for carrying out intermediate results by this SwingWorker's 
//publish and process methods 

    private class mySwingWorker extends javax.swing.SwingWorker<ArrayList<Integer>, Integer> { 
//The first template argument, in this case, ArrayList<Integer>, is what s returned by doInBackground(), 
//and by get(). The second template argument, in this case, Integer, is what is published with the 
//publish method. It is also the data type which is stored by the java.util.List that is the parameter 
//for the process method, which recieves the information published by the publish method. 

     @Override 
     protected ArrayList<Integer> doInBackground() { 
//Returns items of the type given as the first template argument to the SwingWorker class. 
      if (javax.swing.SwingUtilities.isEventDispatchThread()) { 
       System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() returned true."); 
      } 
      Integer tmpValue = new Integer(1); 
      ArrayList<Integer> list = new ArrayList<Integer>(); 
      for (int i = 0; i < 100; i++) { 
       for (int j = 0; j < 100; j++) { //find every 100th prime, just to make it slower 
        tmpValue = FindNextPrime(tmpValue.intValue()); 
//isCancelled() returns true if the cancel() method is invoked on this class. That is the proper way 
//to stop this thread. See the actionPerformed method. 
        if (isCancelled()) { 
         System.out.println("SwingWorker - isCancelled"); 
         return list; 
        } 
       } 
//Successive calls to publish are coalesced into a java.util.List, which is what is received by process, 
//which in this case, isused to update the JProgressBar. Thus, the values passed to publish range from 
//1 to 100. 
       publish(new Integer(i)); 
       list.add(tmpValue); 
      } 
      return list; 
     }//Note, always use java.util.List here, or it will use the wrong list. 

     @Override 
     protected void process(java.util.List<Integer> progressList) { 
//This method is processing a java.util.List of items given as successive arguments to the publish method. 
//Note that these calls are coalesced into a java.util.List. This list holds items of the type given as the 
//second template parameter type to SwingWorker. Note that the get method below has nothing to do with the 
//SwingWorker get method; it is the List's get method. This would be a good place to update a progress bar. 
      if (!javax.swing.SwingUtilities.isEventDispatchThread()) { 
       System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() + returned false."); 
      } 
      Integer percentComplete = progressList.get(progressList.size() - 1); 
      progressBar.setValue(percentComplete.intValue()); 
     } 

     @Override 
     protected void done() { 
      System.out.println("doInBackground is complete"); 
      if (!javax.swing.SwingUtilities.isEventDispatchThread()) { 
       System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() + returned false."); 
      } 
      try { 
//Here, the SwingWorker's get method returns an item of the same type as specified as the first type parameter 
//given to the SwingWorker class. 
       ArrayList<Integer> results = get(); 
       for (Integer i : results) { 
        listModel.addElement(i.toString()); 
       } 
      } catch (Exception e) { 
       System.out.println("Caught an exception: " + e); 
      } 
      startButton(); 
     } 

     boolean IsPrime(int num) { //Checks whether a number is prime 
      int i; 
      for (i = 2; i <= num/2; i++) { 
       if (num % i == 0) { 
        return false; 
       } 
      } 
      return true; 
     } 

     protected Integer FindNextPrime(int num) { //Returns next prime number from passed arg.  
      do { 
       if (num % 2 == 0) { 
        num++; 
       } else { 
        num += 2; 
       } 
      } while (!IsPrime(num)); 
      return new Integer(num); 
     } 
    } 

    private JButton makeButton(String caption) { 
     JButton b = new JButton(caption); 
     b.setActionCommand(caption); 
     b.addActionListener(this); 
     getContentPane().add(b); 
     return b; 
    } 

    private JProgressBar makeProgressBar(int min, int max) { 
     JProgressBar progressBar1 = new JProgressBar(); 
     progressBar1.setMinimum(min); 
     progressBar1.setMaximum(max); 
     progressBar1.setStringPainted(true); 
     progressBar1.setBorderPainted(true); 
     getContentPane().add(progressBar1); 
     return progressBar1; 
    } 

    private void startButton() { 
     startButton.setEnabled(true); 
     stopButton.setEnabled(false); 
     System.out.println("SwingWorker - Done"); 
    } 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     if ("Start" == null ? e.getActionCommand() == null : "Start".equals(e.getActionCommand())) { 
      startButton.setEnabled(false); 
      stopButton.setEnabled(true); 
// Note that it creates a new instance of the SwingWorker-derived class. Never reuse an old one. 
      (swingWorker = new mySwingWorker()).execute(); // new instance 
     } else if ("Stop" == null ? e.getActionCommand() == null : "Stop".equals(e.getActionCommand())) { 
      startButton.setEnabled(true); 
      stopButton.setEnabled(false); 
      swingWorker.cancel(true); // causes isCancelled to return true in doInBackground 
      swingWorker = null; 
     } 
    } 

    public static void main(String[] args) { 
// Notice that it kicks it off on the event-dispatching thread, not the main thread. 
     SwingUtilities.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       SwingWorkerExample swingWorkerExample = new SwingWorkerExample(); 
      } 
     }); 
    } 
} 
+0

有趣。感谢您的解释;列表返回尤其可以为我的下一个项目派上用场。 – DGolberg 2013-03-01 20:40:40