6

Currently I have two SwingWorker threads doing job on background. If an exception occurs, the method stop to work, but the thread still runnig.

How I do to stop the execution and kill the thread of the doInBackground() if an exception occurs?

this.cancel(true) don't destroy/close the thread. How can I achieve this?

@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;
        }
    }

I see these threads in the debug of 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
mKorbel
  • 109,525
  • 20
  • 134
  • 319
Renato Dinhani
  • 35,057
  • 55
  • 139
  • 199

6 Answers6

12

as jzd montioned, there is method cancel(boolean mayInterruptIfRunning); for exapmle

EDIT: with cancel(true); you have to (always) cautgh an exception 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();
            }
        });
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • The isCancelled() method returns true after the cancel() method, but why the thread 'SwingWorker-pool-1-thread-1' still appearing as in execution? – Renato Dinhani May 24 '11 at 18:20
  • good explanation as all given here (+1 all). The example is colossal but I like it thanks :) – Boro May 25 '11 at 11:04
  • This one interestingly takes the help of a `JProgressBar` to explain the logic thingy, nice one :-) – nIcE cOw Jul 20 '13 at 11:00
  • after clicking cancel it doesn't output incomplete result to listModel. Log: `doInBackground is complete` `SwingWorker - Done` `doInBackground is complete` `SwingWorker - isCancelled` `Caught an exception: java.util.concurrent.CancellationException` `SwingWorker - Done` – yalov May 31 '19 at 13:49
  • found there https://stackoverflow.com/questions/6204141/swingworker-when-exactly-is-called-done-method, 'done()' called not in the expected time, then you cancel swingworker – yalov May 31 '19 at 13:56
10

By default, SwingWorker reuses worker threads, so it is perfectly normal that, even though, doInBackground() has returned, to still see the thread that executed your method.

You can identify this fact by looking at the thread names, as reported by NetBeans: SwingWorker-pool-1-thread-1, where that pool is managed by SwingWorker.

If you want more control, you can also pass the SwingWorker instance to an Executor.

Just check SwingWorker and Executor javadoc for further information.

Besides, SwingWorker.cancel() is not supposed to be called from doInBackground() but rather from another thread, generally the EDT, e.g. when user clicks a Cancel button in a progress dialog.

Pascal Schneider
  • 405
  • 1
  • 5
  • 19
jfpoilpret
  • 10,449
  • 2
  • 28
  • 32
  • It's not reusing, each time I click on the button to the process that will run in background it creates a new thread. I will try call from another thread to see what happens. – Renato Dinhani May 24 '11 at 19:56
  • Hard to tell, but you can't master the way `SwingWorker` deals with thread pools and assignment of a given `SwingWorker` instance to a thread in the pool. As I said, if you want full control, then you can use one of the existing implementations of `Executor`, or build your own, and pass your `SwingWorker` instances to its `execute` method. – jfpoilpret May 24 '11 at 20:16
  • agreed, but Executor plus SwingWorker 1) still in top25 http://bugs.sun.com/top25_bugs.do 2) if you want really Multithreading, then you have to Naming your threads 3) add PropertyChangeListener for SwingWorker, then you can know statuses of SwingWorker 4) be caffefull with number of simultaneous Threads started from Executor, because Executor doesn't know if SwingWorker ends or not 5) to avoid any of failtures from put here boolean boolean value, then only check value from PropertyChangeListener with last line from done(), that returns true a confirmed that thread ends without any fauls – mKorbel May 24 '11 at 20:25
  • @mKorbel the top bug of SwingWorker is unrelated to using it with an Executor if I remember well, it's in fact a "pure" SwingWorker bug. – jfpoilpret May 24 '11 at 20:40
  • Yes, after all I guess I will abandon SwingWorker and find another way. I just was curious about the SwingWorker, but looks if I need full control, is better using Executors or another thing. Thanks to all. – Renato Dinhani May 24 '11 at 21:05
  • Executor is a simple interface, I don't see how it could be buggy ;-) What I said is that you can use a SwingWorker with an Executor (rather than just calling execute() on it), and you can implement your own Executor if the standard ones don't work or don't fit your needs. Just my (last) 2 cents. – jfpoilpret May 25 '11 at 11:39
4

There is a cancel() method. Your code would have to pay attention to that. If it keeps running after an exception, it would appear that you code is ignoring an exception that it shouldn't be.

jzd
  • 23,473
  • 9
  • 54
  • 76
  • Yes, I see something like this, but these method is called inside the exception with an this.cancel(true); or in class that calls the execute method of SwingWorker? – Renato Dinhani May 24 '11 at 16:56
  • 1
    The cancel method is meant for the objects that call execute. If the SwingWorker needs to stop executing because of an Exception, then that Exception needs to be handled correctly. – jzd May 24 '11 at 16:58
4

FYI for anyone passing through: when a SwingWorker is canceled, done() is called before doInBackground() returns.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6826514

Jeff

AlexElin
  • 1,044
  • 14
  • 23
2

You need to add Thread.sleep(1) calls in your doInBackground() code every so often. In your sleep catch blocks, add return. I had the same problem, and this worked like a charm.

Source: https://blogs.oracle.com/swinger/entry/swingworker_stop_that_train

Joel Christophel
  • 2,604
  • 4
  • 30
  • 49
0

Till The SwingWoker it fixed http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6826514 Here a simple (tested) version with the basic (similar) functions then 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();
        }
    }

}
pknoe3lh
  • 374
  • 3
  • 10