Stop/cancel SwingWorker thread?
Asked Answered
B

4

27

I'm trying to find out how to stop a SwingWorker thread from running when I press a button. I have been looking around and I'm having some trouble working out how to do this. At the moment this is what I have:

new MySwingWorkerClass(args).execute();

I'm then creating a button which I want to use in order to stop the thread:

button = new JButton("Stop");
button.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) 
        {
        // Stop the swing worker thread
        }
});

I have already looked around in search of an answer, so far I have managed to find the cancel method. I don't understand how to use this to stop my swing worker though. I tried the following but it didn't work:

SwingWorker.cancel(true);
Babb answered 10/11, 2011 at 17:31 Comment(1)
cancel is not a static method, so the last line doesn't even compile .. keep a reference to the instance (as @Jonas suggested) it the way to goBinomial
P
45

You need to keep a reference to your SwingWorker, then you use that reference to cancel the worker thread.

MySwingWorker myWorker = new MySwingWorkerClass(args).execute();

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) 
    {
        // Stop the swing worker thread
        myWorker.cancel(true);
    }
});

Here is a full example:

enter image description here

public class WorkerDemo extends JFrame {
    private boolean isStarted = false;
    private JLabel counterLabel = new JLabel("Not started");
    private Worker worker = new Worker();
    private JButton startButton = new JButton(new AbstractAction("Start") {

        @Override
        public void actionPerformed(ActionEvent arg0) {
            if(!isStarted) { 
                worker.execute();
                isStarted = false;
            }
        }

    });
    private JButton stopButton = new JButton(new AbstractAction("Stop") {

        @Override
        public void actionPerformed(ActionEvent arg0) {
            worker.cancel(true);
        }

    });

    public WorkerDemo() {

        add(startButton, BorderLayout.WEST);
        add(counterLabel, BorderLayout.CENTER);
        add(stopButton, BorderLayout.EAST);
        pack();
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }

    class Worker extends SwingWorker<Void, Integer> {

        int counter = 0;

        @Override
        protected Void doInBackground() throws Exception {
            while(true) {
                counter++;
                publish(counter);
                Thread.sleep(60);
            }
        }

        @Override
        protected void process(List<Integer> chunk) {

            // get last result
            Integer counterChunk = chunk.get(chunk.size()-1);

            counterLabel.setText(counterChunk.toString());
        }

    }

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

            @Override
            public void run() {
                new WorkerDemo();
            }

        });
    }

}
Pamalapamela answered 7/2, 2012 at 18:51 Comment(4)
I'm curious as to why this answer hasn't been accepted. It seems like the correct and most obvious one.Lodi
just noticed a little typo: in startButton, you probably want to set the isStarted property to true :-)Binomial
actually, it doesn't make a difference: api doc states SwingWorker is only designed to be executed once. Executing a SwingWorker more than once will not result in invoking the doInBackground method twiceBinomial
does swingworker.cancel(true) ensure that the swingworker is cancelled/killed? if I put a while loop in there and try to cancel the worker from main class, the loop doesn't stopTrangtranquada
M
16

You need to periodically check its cancelled flag (i.e. isCancelled()). SwingWorker leaves how to handle the interrupt up to you.

For more information, see Cancelling Background Tasks.

Mcglone answered 10/11, 2011 at 17:40 Comment(6)
example for isCancelled() #6114444 , +1Imbibe
@Mcglone thankyou for your help. Unfortunately I'm still confused as to the best approach since it's not possible to regularly check the status of isCancelled in my code. Is there a way I can just terminate it completely?Babb
I would post it, however there a lots of classes. Basically, I call a method that runs some code in a loop, this can take several hours to run since there's a lot of processing of data to be done. This is exactly the reason I need a stop button to somehow kill the thread or interrupt it or something.Babb
@TheCrazyChimp, You need to intermittently check the canceled flag, otherwise you'll never know that you've been requested to stop.Mcglone
@TheCrazyChimp "I call a method that runs some code in a loop". You can do one of two things: 1) Call isCancelled() somewhere in that loop (you might need to do some refactoring so that your method which does the looping has access to the SwingWorker instance). 2) Call Thread.sleep() somewhere in that loop. (This works because Thread.sleep() checks whether the current thread has been cancelled.)Solorzano
Thank you for this link - the while(!isCancelled()) {...}-Loop helped me alot!Disenthrone
C
2

you can try overidding the done() method and put it in try catch and catch the java.util.concurrent.CancellationException. Something like

. . .

@Overide
done() {
   try {
     get();
   } catch (CancellationException e) {
       // Do your task after cancellation
   }
}

. . .

Campanulate answered 14/2, 2012 at 15:21 Comment(1)
It is generally considered bad practice to use Exceptions for program flow. ActionListeners would probably be a better solution.Iceland
F
2

I think you have to restart the counter after every stop.

For startButton, you have to have worker restarted as in

private JButton startButton = new JButton(new AbstractAction("Start") {

        @Override
        public void actionPerformed(ActionEvent arg0) {
            if(!isStarted) { 
                worker = new Worker();     // New line
                worker.execute();
                isStarted = false;
            }
        }
    });

To stop you can use

worker.setStopFlag(); // in stopButton actionPerformed block.

in Worker class private boolean stopFlag = false;

and add

if( stopFlag)
break;

after Thread.sleep(60); and finally put setter for stopFlag at the end of Worker class as

void setStopFlag(){
    stopFlag = true;
}

By the way, you can use cancel(true) if you want to have exception exit.

Fruitage answered 5/2, 2013 at 6:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.