2011-05-24 3 views
6

В настоящее время у меня есть две темы SwingWorker, работающие на фоне. Если возникает исключение, метод перестает работать, но поток все еще запущен.Как отменить выполнение SwingWorker?

Как сделать, чтобы остановить выполнение и убить поток doInBackground(), если возникло исключение?

this.cancel(true) не разрушать/закрывать нить. Как я могу это достичь?

@Override 
protected Boolean doInBackground() throws Exception { 
     try { 
      while (true) { 
       //some code here     
       return true; 
      } 
     } catch (Exception e) {  
      this.cancel(true); //<-- this not cancel the thread    
      return false; 
     } 
    } 

Я вижу эти потоки в отладке Netbeans.

'AWT-EventQueue-0' em execução 
'AWT-Windows' em execução 
'SwingWorker-pool-1-thread-1' em execução 
'SwingWorker-pool-1-thread-2' em execução 

//*em execução = in execution 
+0

Можете ли вы подтвердить, что 'doInBackground()' возвращает и не блокирует внутри цикла while (true)? – jfpoilpret

+0

Да, я отлаживал код и в него включается исключение, возвращающее false. –

ответ

7

По умолчанию SwingWorker многократно использующую рабочие потоки, так что это совершенно нормально, что, несмотря на то, doInBackground() вернулся, чтобы еще увидеть поток, который выполняется ваш метод.

Вы можете определить этот факт, глядя на имена нитей, как сообщает NetBeans: SwingWorker-pool-1-thread-1, где что бассейн управляется SwingWorker.

Если вы хотите большего контроля, вы также можете передать пример SwingWorker на номер Executor.

Просто уточните SwingWorker и Executor javadoc для получения дополнительной информации.

Кроме того, SwingWorker.cancel() не должен вызываться от doInBackground(), а скорее из другого потока, обычно EDT, например. когда пользователь нажимает кнопку Отмена в диалоговом окне прогресса.

+0

Это не повторное использование, каждый раз, когда я нажимаю кнопку на процесс, который будет работать в фоновом режиме, создавая новый поток. Я попробую позвонить из другого потока, чтобы узнать, что произойдет. –

+0

Трудно сказать, но вы не можете справиться с тем, как SwingWorker занимается пулами потоков и назначением данного экземпляра 'SwingWorker' потоку в пуле. Как я уже сказал, если вы хотите получить полный контроль, вы можете использовать одну из существующих реализаций «Executor» или создать свою собственную и передать свои экземпляры SwingWorker в свой метод 'execute'. – jfpoilpret

+0

согласился, но Executor plus SwingWorker 1) все еще в топ25 http://bugs.sun.com/top25_bugs.do 2) если вы действительно хотите многопоточность, тогда вам нужно называть ваши потоки 3) добавить PropertyChangeListener для SwingWorker, тогда вы можете знать статусы SwingWorker 4) быть caffefull с количеством одновременных потоков, начатых с Executor, потому что Executor не знает, заканчивается ли SwingWorker или нет 5), чтобы избежать каких-либо ошибок с put здесь boolean boolean value, а затем проверять значение только с PropertyChangeListener с последним line from done(), который возвращает true, подтвердил, что нить заканчивается без каких-либо ошибок – mKorbel

4

Метод cancel(). Ваш код должен был бы обратить на это внимание. Если он будет работать после исключения, будет казаться, что код игнорирует исключение, которого не должно быть.

+0

Да, я вижу нечто подобное, но этот метод вызывается внутри исключения с помощью this.cancel (true); или в классе, который вызывает метод выполнения SwingWorker? –

+1

Метод отмены предназначен для объектов, которые вызывают выполнение. Если SwingWorker должен прекратить выполнение из-за исключения, то это Исключение должно быть обработано правильно. – jzd

9

as jzd montioned, есть способ cancel(boolean mayInterruptIfRunning); для exapmle

EDIT: с отменой (true); вы должны (всегда) cautgh исключения java.util.concurrent.CancellationException

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

Метод isCancelled() возвращает true после метода cancel(), но почему поток SwingWorker-pool-1-thread-1 по-прежнему отображается как в исполнении? –

+0

Появляется «SwingWorker-pool-1-thread-2». –

+0

хорошее объяснение как все данный здесь (+1 все). Пример колоссален, но мне это нравится. :) – Boro

2

Вы должны добавить Thread.sleep(1) вызовы в коде doInBackground() каждый так часто. В блоках блокировки сна добавьте return. У меня была такая же проблема, и это сработало как шарм.

Источник: https://blogs.oracle.com/swinger/entry/swingworker_stop_that_train

+1

Спасибо, что поделились, это было очень полезно! – DSquare

0

До SwingWoker это фиксированный http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6826514 Вот простой (протестировано) версии с основными (аналогичными) функций, тогда SwingWoker

/* 
* To change this template, choose Tools | Templates 
* and open the template in the editor. 
*/ 
package tools; 

import java.util.LinkedList; 
import java.util.List; 
import javax.swing.SwingUtilities; 

/** 
* 
* @author patrick 
*/ 
public abstract class MySwingWorker<R,P> { 

    protected abstract R doInBackground() throws Exception; 
    protected abstract void done(R rvalue, Exception ex, boolean canceled); 
    protected void process(List<P> chunks){} 
    protected void progress(int progress){} 

    private boolean cancelled=false; 
    private boolean done=false; 
    private boolean started=false; 
    final private Object syncprogress=new Object(); 
    boolean progressstate=false; 
    private int progress=0; 
    final private Object syncprocess=new Object(); 
    boolean processstate=false; 
    private LinkedList<P> chunkes= new LinkedList<>(); 

    private Thread t= new Thread(new Runnable() { 
     @Override 
     public void run() { 
      Exception exception=null; 
      R rvalue=null; 
      try { 
       rvalue=doInBackground(); 
      } catch (Exception ex) { 
       exception=ex; 
      } 

      //Done: 
      synchronized(MySwingWorker.this) 
      { 
       done=true; 
       final Exception cexception=exception; 
       final R crvalue=rvalue; 
       final boolean ccancelled=cancelled; 

       SwingUtilities.invokeLater(new Runnable() { 
        @Override 
        public void run() { 
         done(crvalue, cexception, ccancelled); 
        } 
       }); 
      } 

     } 
    });  

    protected final void publish(P p) 
    { 
     if(!Thread.currentThread().equals(t)) 
      throw new UnsupportedOperationException("Must be called from worker Thread!"); 
     synchronized(syncprocess) 
     { 
      chunkes.add(p); 
      if(!processstate) 
      { 
       processstate=true; 
       SwingUtilities.invokeLater(new Runnable() { 
        @Override 
        public void run() { 
         List<P> list; 
         synchronized(syncprocess) 
         { 
          MySwingWorker.this.processstate=false; 
          list=MySwingWorker.this.chunkes; 
          MySwingWorker.this.chunkes= new LinkedList<>(); 
         } 
         process(list); 
        } 
       }); 
      } 
     } 
    } 

    protected final void setProgress(int progress) 
    { 
     if(!Thread.currentThread().equals(t)) 
      throw new UnsupportedOperationException("Must be called from worker Thread!"); 
     synchronized(syncprogress) 
     { 
      this.progress=progress; 
      if(!progressstate) 
      { 
       progressstate=true; 
       SwingUtilities.invokeLater(new Runnable() { 
        @Override 
        public void run() { 
         int value; 
         //Acess Value 
         synchronized(syncprogress) 
         { 
          MySwingWorker.this.progressstate=false; 
          value=MySwingWorker.this.progress; 
         } 
         progress(value); 
        } 
       }); 
      } 
     } 
    } 

    public final synchronized void execute() 
    { 
     if(!started) 
     { 
      started=true; 
      t.start(); 
     } 
    } 

    public final synchronized boolean isRunning() 
    { 
     return started && !done; 
    } 

    public final synchronized boolean isDone() 
    { 
     return done; 
    } 

    public final synchronized boolean isCancelled() 
    { 
     return cancelled; 
    } 

    public final synchronized void cancel() 
    { 
     if(started && !cancelled && !done) 
     { 
      cancelled=true; 
      if(!Thread.currentThread().equals(t)) 
       t.interrupt(); 
     } 
    } 

} 
Смежные вопросы