26

From java docs on Future.cancel()

boolean cancel(boolean mayInterruptIfRunning)

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. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.

My question is what does cancel do if mayInterruptIfRunning is false?
how does it cancel or stop the task from executing if it has already been run?

Tom
  • 1,203
  • 4
  • 21
  • 36

3 Answers3

19

If it is not interrupting it will simply tell the future that is is cancelled. You can check that via isCancelled() but nothing happens if you don't check that manually.

Below example code shows how you could do it.

private static FutureTask<String> task = new FutureTask<String>(new Callable<String>() {

    @Override
    public String call() throws Exception {
        while (!task.isCancelled()) {
            System.out.println("Running...");
            Thread.sleep(1000);
        }
        return "The End";
    }

});

public static void main(String[] args) throws InterruptedException {
    new Thread(task).start();
    Thread.sleep(1500);
    task.cancel(false);
}

The task is started, and after 1.5 iterations told to stop. It will continue sleeping (which it would not if you interrupted it) and then finish.

spongebob
  • 8,370
  • 15
  • 50
  • 83
zapl
  • 63,179
  • 10
  • 123
  • 154
  • but i can't "know" that from within the object?? (Future) – Tom Jan 29 '14 at 23:58
  • 1
    Note that there would be no way of checking `isCancelled()` from e.g. `Callable.call` - it could only check if it was interrupted. – Paul Bellora Jan 29 '14 at 23:59
  • @PaulBellora see updated answer. You need to add some way to reference the `Future` from within the `Callable` to make that work. – zapl Jan 30 '14 at 00:02
  • @zapl Okay but that seems a little weird and self-defeating. `while (!Thread.interrupted()) {` would make more sense. – Paul Bellora Jan 30 '14 at 00:05
  • 1
    Yes, I typically do that and say `task.cancel(true)` because that's accessible via `Thread.interrupted()`. With `false` you will never get `Thread.interrupted()` return true because it does not interrupt. I have not found much use for `cancel(false)` or a pattern how you don't have to use ugly references to the outside either :) – zapl Jan 30 '14 at 00:08
  • @zapl Right, it seems like `cancel(false)` would be for preventing it from *starting*, but if you didn't want to interrupt it otherwise. – Paul Bellora Jan 30 '14 at 00:12
  • It's probably much more useful for that since it is really tricky to come up with a nice way to be able to check it otherwise. You could potentially want that your thread is not interrupted though and just ended once you decide to do so. http://pastebin.com/1QPvVVkF is already a tad nicer but still not great. – zapl Jan 30 '14 at 00:25
  • Eclipse 4.6 doesn't let you do this. The expression `!task.isCancelled()` gives the un-ignoreable error "The local variable task may not have been initialized". Since task has to be final or effectively final, you're stuck. – Luke Hutchison Jul 15 '16 at 11:15
  • @LukeHutchison works fine for me. `task` is not a local variable, it's a field of the enclosing class. – zapl Jul 19 '16 at 08:29
6

nothing will be done if the task has already started and mayInterruptIfRunning is false,

below is the cancel()

public boolean cancel(boolean mayInterruptIfRunning) {
    if (state != NEW)
        return false;
    if (mayInterruptIfRunning) {
        if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING))
            return false;
        Thread t = runner;
        if (t != null)
            t.interrupt();
        UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state
    }
    else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED))
        return false;
    finishCompletion();
    return true;
}

we can see that if mayInterruptIfRunning is false,cancel() just change state from NEW to CANCELLED and return false,nothing else will be done

Slogen
  • 61
  • 1
  • 2
3

My question is what does cancel do if mayInterruptIfRunning is false? how does it cancel or stop the task from executing if it has already been run?

If the task has already started running, and mayInterruptIfRunning is false, then there is nothing to be done. In Java, interrupting a thread is considered to be the only safe way of stopping it short of completion - and even that requires the task to "comply" by checking for interruption at some implementation-specific interval.

Related: How do you kill a thread in Java?

Community
  • 1
  • 1
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181