How cancel the execution of a SwingWorker?
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.
Stop/cancel SwingWorker thread?
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:
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();
}
});
}
}
SwingWorker, Cancel-Button doesn't work
Maybe if you use while
instead of if
on doInBackground()
method of Worker
class you will solve your problem. You must to put out of the while
loop the mapp()
, because you only want to invoke it one time. You should do something like this:
class Worker extends SwingWorker<Void, Void> {
@Override
protected Void doInBackground() throws Exception {
mapp();
while(!isCancelled()){
Thread.sleep(60);
}
System.out.println("SwingWorker - isCancelled");
return null;
}
This link could be useful to understanding how to use SwingWorker
.
EDIT:
As you can see on another questions like this or this, using SwingWorker
has some problems to manage the cancel
method, because this method Attempts to cancel execution of this task. This attempt will fail if the task has already completed, has already been cancelled, or could not be cancelled for some other reason, like Oracle explains, and those "some other reasons" are discussed on the links I've posted.
You can do solve your problem using directly Threads
. Your code would be something like this:
public class MainWindow extends JFrame implements ActionListener, WindowListener
{
// Some code, like generating JFrame, JButtons and other stuff not affencting the task.
final Thread th1 = new Thread(new Runnable() {
@Override
public void run() {
mapp();
}
});
public void actionPerformed(ActionEvent e)
{
boolean isStarted = false;
// Start Button
if (e.getSource() == this.buttonStart)
{
if(!isStarted)
{
System.out.println("start");
labelSuccess.setText("Mapping started!");
this.setEnabled(true);
th1.start();
isStarted = false;
}
}
// Stop Button
if (e.getSource() == this.buttonStop)
{
labelSuccess.setText("Mapping stopped!");
th1.stop();
}
}
This solutions uses the method stop()
, which is deprecated, but it works. I've tried using interrupt()
, but I don't know why the thread ran till finish the execution of mapp()
. Obviously, using stop()
is not the best method but it works stopping the mapp()
execution before it finishes.
I recommend you to learn more about SwingWorker
, Thread
and Task
to find the best solution to your problem.
Stop SwingWorker with stop button
Start by creating an instance field to which you can maintain a reference to the SwingWorker
...
public class rungui extends JFrame {
private SwingWorker<String, Void> worker;
//..
Change your start
method to assign an instance to it...
void start() {
// Check to see if the worker is already running...
if (worker == null || worker.isDone() || worker.isCancelled()) {
worker = new SwingWorker<String, Void>() {
@Override
protected String doInBackground() throws Exception {
//...
}
protected void done() {
worker = null;
//...
}
};
//...
Then, you need to monitor the isCancelled
state of the SwingWorker
.
It's important that you check before you do any "significant" work, as some blocking functionality may not be interruptible
@Override
protected String doInBackground() throws Exception {
useragentwa = new read();
userAgent = useragentwa.close();
Document page = null;
boolean shouldContinue = !isCancelled() || test;
while (shouldContinue) {
if (isCancelled()) {
shouldContinue = false;
continue;
}
finalDomain = http + "" + start_index + "" + domain;
check = start_index % 20;
if (check == 0) {
useragentwa = new read();
userAgent = useragentwa.close();
System.out.println(userAgent);
}
if (isCancelled()) {
shouldContinue = false;
continue;
}
start_index++;
System.out.println(start_index);
try {
page = Jsoup.connect(finalDomain).userAgent(userAgent).timeout(10 * 1000).get();
} catch (Exception e) {
start_index--;
continue;
}
if (isCancelled()) {
shouldContinue = false;
continue;
}
if (page.title().contains("U.S. Directory - Online Yellow Pages")) {
// area may want to append in console text area
continue;
} else {
System.out.println("found something : " + finalDomain);
test = false;
shouldContinue = false;
continue;
}
}
return finalDomain;
}
And finally, you need to call cancel
on the instance of the SwingWorker
...
stop_button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(worker != null && !worker.isCancelled() && !worker.isDone()) {
worker.cancel(true);
}
}
});
To be honest, this is just programing 101
How to Stop a SwingWorker?
This is not a answer to the question (which is how to cancel, I think that's solved) but how to separate the background handling from the view/model updates. A pseudo-code outline:
public static class MyDBWorker extends SwingWorker<Void, Object[]> {
private JTable table;
private DefaultTableModel model;
private ResultSet resultado;
public MyDBWorker(JTable table, ResultSet resultado) {
this.table = table;
this.resultado = resultado;
}
@Override
protected Void doInBackground() throws Exception {
ResultSetMetaData metadata = resultado.getMetaData();
int columnas = metadata.getColumnCount();
Object[] etiquetas = new Object[columnas];
for (int i = 0; i < columnas; i++) {
etiquetas[i] = metadata.getColumnName(i + 1);
}
publish(etiquetas);
while (resultado.next() && !this.isCancelled()) {
Object fila[] = new Object[columnas];
for (int i = 0; i < columnas; i++) {
fila[i] = resultado.getObject(i + 1);
}
publish(fila);
}
return null;
}
@Override
protected void process(List<Object[]> chunks) {
int startIndex = 0;
// first chunk, set up a new model
if (model == null) {
model = new DefaultTableModel();
model.setColumnIdentifiers(chunks.get(0));
table.setModel(model);
startIndex = 1;
}
for (int i = startIndex; i < chunks.size(); i++) {
model.addRow(chunks.get(i));
}
}
@Override
protected void done() {
// nothing to do, we were cancelled
if (isCancelled()) return;
// check for errors thrown in doInBackground
try {
get();
// all was well in the background thread
// check if there are any results
if (table.getModel().getRowCount() == 0) {
// show message
}
} catch (ExecutionException e) {
// we get here if f.i. an SQLException is thrown
// handle it as appropriate, f.i. inform user/logging system
} catch (InterruptedException e) {
//
}
}
}
// use it
SwingWorker worker = new MyDBWorker(table, resultado);
PropertyChangeListener stateListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent e) {
if (e.getNewValue() == SwingWorker.StateValue.STARTED) {
progressBar.setIndeterminate(true);
}
if (e.getNewValue() == SwingWorker.StateValue.DONE) {
progressBar.setIndeterminate(false);
}
}
};
worker.addPropertyChangeListener(doneListener);
worker.execute();
Edit
fixed a bug in process: adding rows to the model must start on first index of the batch always, except when processing the header.
Cancel SwingWorker clearly with session
public void actionPerformed(ActionEvent arg0) {
if(psw != null && !psw.isDone()){
closeDBConnection(psw.getSession());
psw.cancel(true);
psw = null;
restoreData();
}
This sure looks to me like you're closing the db connection while the reader thread is still running, which would probably result in exactly what you're seeing. You need to restructure things so the reader thread responds to the cancel and closes the connection itself in a finally block.
SwingWorker: error thrown when done() is called after worker thread is cancelled
Your solution is imperfect since cancellation could occur between the time you call isCancelled() and the time you call get() which would, rarely, still result in the exception. Better would be to catch the CancellationException and handle it.
Related Topics
Best Way to Represent a Fraction in Java
Solve Hibernate Lazy-Init Issue with Hibernate.Enable_Lazy_Load_No_Trans
How to Create Correct JSONarray in Java Using JSONobject
Converting Many 'If Else' Statements to a Cleaner Approach
@Runwith(Mockitojunitrunner.Class) VS Mockitoannotations.Initmocks(This)
Spring Configuration Xml Schema: with or Without Version
Java 8's Streams: Why Parallel Stream Is Slower
How Does the Jvm Decided to Jit-Compile a Method (Categorize a Method as "Hot")
String to String Array Conversion in Java
Finding Key Associated with Max Value in a Java Map
Java - Get a List of All Classes Loaded in the Jvm
When Would You Call Java's Thread.Run() Instead of Thread.Start()
Reliable File.Renameto() Alternative on Windows
&& (And) and || (Or) in If Statements