2

I'm currently trying to understand how FutureTask.cancel(true) is working, this is the relevant piece of official doc

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.

And this is the implementation of cancel taken from Github

public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW &&
          UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
              mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    try {    // in case call to interrupt throws exception
        if (mayInterruptIfRunning) {
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally { // final state
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
        finishCompletion();
    }
    return true;
}

So, basically we can see that the only thing that cancel(true) is doing is calling interrupt on the worker thread. So, what if the call method of my FutureTask looks like something like this

SomeType call() {
  for(int i = 0; i < 1000000000; i++) {
    //pass
  }
  return someValue;
}

So, my question - do I have to add manual check for thread interruption in order to be able to cancel FutureTasks of such type ? I mean it seems to be obvious because I'm not calling any IO function can handle interrupt and not checking Thread.currentThread().isInterrupted(), so this task seems to be no cancelable, but still this is not mentioned in any official doc that we have to handle interrupts or self to be able to cancel the task so it still better to ask others opinion.

starwarrior8809
  • 361
  • 1
  • 10

2 Answers2

1

Yes, you have to handle the CancellationException (this exception throws the FutureTask.get() method), if you don't do it, this exception will be thrown to the JVM. Look through this example, it will output "Task is canceled":

public class MainTest {
    public static void main(String... args) {
        FutureTask<Long> futureTask = new FutureTask<>(new MyCallable());

        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.submit(futureTask);

        try {
            futureTask.cancel(true);
            System.out.println(futureTask.get());
        } catch (CancellationException e) {
            System.out.println("Task is canceled");
        } catch (InterruptedException | ExecutionException e) {
            System.out.println("Something went wrong!");
        }

        executorService.shutdown();
    }
}

class MyCallable implements Callable<Long> {
    @Override
    public Long call() {
        for(int i = 0; i < 1000000000; i++) {
            //pass
        }

        return 1L;
    }
}
  • thank you for the reply, but actually it is not related to the question. In your example you never start the execution of `MyCallable`, so you cancel it before it is actually started. So, in you example it doesn't really matter if you pass `true` or `false` to cancel since task is never executed. And my question is related to canceling already ongoing task and handling interrupt directly in `call` – starwarrior8809 Mar 01 '21 at 12:51
  • Oh, yes i forgot start the task. Sorry, i wrong understood the question, and i have no idea how to handle this in call method... – Vladsmirn289 Mar 01 '21 at 13:26
1

There's no pre-emptiveness in Java. For anything to be interruptable, it has to cooperate. So yes, the task must check if it's been interrupted or else it will run till the end despite the future being cancelled. Disappointing, I know.

kaqqao
  • 12,984
  • 10
  • 64
  • 118