5

I would like to cancel a running task and replace it with a new one, on the same thread.

In the code below, I used a single-thread executor to start a new task and capture a Future representing it. I then used the Future to cancel the task. However, the task did not cancel.

  1. Why does my code not work?
  2. How do I cancel a running task and replace it with a new one, on the same thread?

Example code:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

class Task implements Runnable {
    public void run() {
        System.out.println("New task started");
        int i = 0; while (true) i++;
    }
}

public class TaskLauncher extends JFrame {
    private JButton button = new JButton("Start new task");
    private ExecutorService executor = Executors.newSingleThreadExecutor();
    private Future<?> future;

    public TaskLauncher() {
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                if (future != null) future.cancel(true);
                future = executor.submit(new Task());
            }
        });

        add(button);

        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new TaskLauncher();
            }
        });
    }
}
kleopatra
  • 51,061
  • 28
  • 99
  • 211
The Aviv
  • 345
  • 1
  • 2
  • 11

1 Answers1

6

Just calling cancel on the Future object will not cause the task to stop. See the API docs for more information. Also check this SO Question which shows how you have to code your task so that it can be cancelled using the Future object.

Modify your task like this:

class Task implements Runnable {
    public void run() {
        System.out.println("New task started");
        int i = 0; 
        while (true) {
            i++;
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("Exiting gracefully");
                return;
            }
        }
    }
}
Community
  • 1
  • 1
Suresh Kumar
  • 11,241
  • 9
  • 44
  • 54
  • 2
    that's incorrect! you should call the isInterrupted() method *not* intrrupted() as it'll reset the interrupt flag. I prefer to write into it into the while, ie, while (!Thread.isInterrupted()) then you can avoid the multiple branch returns and condition check above... – Toby Mar 13 '12 at 15:21
  • How does resetting the interrupt flag affect OP's solution? Also note that isInteruppted() is an instance method so Thread.isInterrupted() is wrong. I agree the test could be part of while instead of a separate if. – Suresh Kumar Mar 14 '12 at 02:46
  • 1
    my mistake, it should be while (!Thread.currentThread().isInterrupted()). calling intrrupted() isn't incorrect for OPs problem, I should have been clearer that its generally bad practice but not necessarily incorrect. – Toby Mar 14 '12 at 08:07