future.cancel does not work

When you cancel a Future whose Runnable has already begun, the interrupt method is called on the Thread that is running the Runnable. But that won't necessarily stop the thread. Indeed, if it's stuck in a tight loop, like the one you've got here, the Thread won't stop. In this case, the interrupt method just sets a flag called the "interrupt status", which tells the thread to stop when it can.

See the Javadoc for the interrupt method of Thread


The problem is that your Runnable is not interruptible: task interruption is a collaborative process in Java and the cancelled code needs to check regularly if it's been cancelled, otherwise it won't respond to the interruption.

You can amend you code as follows and it should work as expected:

Runnable r = new Runnable() {
    @Override public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()) {}
        } finally {
            System.out.println("FINALLY");
        }
    }
};

Future.cancel() will cancel any queued task or will call Thread.interrupt() on your thread if already running.

You need to interrupt your code

It's your code's responsibility is to be ready for any interruptions. I'd go so far to say that whenever you have a long running task, that you insert some interrupt ready code like this:

while (... something long...) {

     ... do something long

     if (Thread.interrupted()) {
         ... stop doing what I'm doing...
     }
}

How to stop what I'm doing?

You have several options:

  1. If your you are in Runnable.run() just return or break out of the loop and finish the method.
  2. You may be in some other method deep in the code. It may make sense at that point for that method to throw InterruptedException so you would just do that (leaving the flag cleared).
  3. But maybe deep in your code it doesn't make sense to throw InterruptedException. In that case you should throw some other exception, but before that mark your thread interrupted again so the code that catches knows that an interrupt was in progress. Here's an example:
private void someMethodDeepDown() {
    while (.. long running task .. ) {
          ... do lots of work ...

          if (Thread.interrupted()) {
             // oh no! an interrupt!
             Thread.currentThread().interrupt();
             throw new SomeOtherException();
          }
     }
}

Now the exception can propagate an either terminate the thread or be caught, but the receiving code hopefully notices that an interrupt is in progress.


This is always a little bit misleading: The ExceutorService or even the underlying thread scheduler do not know anything about what the Runnable is doing. In your case they don't know that there is a unconditional loop.

All these methods (cancel, done, ...) are related to manage Threads in the Executor structure. cancel cancels the thread from the point of view of the Executor service.

The programmer must test if the Runnable was canceled and must terminate the run() method.

So in your case (if I remember well) something like this:

public class Test {

public static void main(String[] args) {

    FutureTask r = new FutureTask () {
        @Override
        public void run() {
            try {
                for (;!isCancelled();) {

                }
            } finally {
                System.out.println("FINALLY");
            }

        }
    };

    ExecutorService executor = Executors.newSingleThreadExecutor();

    Future<?> future = executor.submit(r);
    try {
        future.get(3, TimeUnit.SECONDS);
    } catch (TimeoutException e) {
        boolean c = future.cancel(true);
        System.out.println("Timeout " + c);
    } catch (InterruptedException | ExecutionException e) {
        System.out.println("interrupted");
    }
    System.out.println("END");

}

}