Java SwingWorker不会在任务完成时终止

好的,所以我一直在玩SwingWorker并获得一些简化的代码来更新gui,但是我很难弄清楚如何让线程在完成时正确终止。 目前,它只通过停止选项终止。 如何设置它以在完成其进程时正确终止线程? 目前, return null; 它进入包装线并挂起。

我的代码如下:

  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 { @Override protected Void doInBackground() { int prog1 = 0; int prog2 = 0; Random random = new Random(); while (prog2 = 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 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()方法,但它从不尝试运行它,它总是只到包行(当单步执行调试器时)并挂起。 我应该返回null以外的值吗?

在此先感谢您的帮助!

我不确定你想要达到的目标。 你的例子工作正常。 工作线程一直运行到最后。 如果你想等到它结束做某事,你必须在代码中的某处调用方法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"); } 

这种变化只是为了说明差异。 为了编写实际代码,我们需要更多地了解您要实现的目标。


如果我的超感官技巧没问题,那么你可能想要将按钮翻回“开始”。 为此,您需要在Worker中覆盖done()方法:

 private class PBTask extends SwingWorker { @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 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"); } } 

例如

 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 - 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, Integer> { //The first template argument, in this case, ArrayList, 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 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 list = new ArrayList(); 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 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 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(); } }); } }