Task Cancellation

Task cancellation is an important aspect of controlling task execution. Here we look at scenarios for cancelling tasks by invoking the cancel() method of the Future<V> object that represents the task after the task has been submitted to the executor. The effect of the cancel() method on the task depends on what stage the task is in when the cancel() method is called.

  • The task is in the queue, waiting to be assigned to a thread.

Calling the cancel() method on the Future<V> object that represents the task removes the task from the queue—thus cancelling the task.

  • The task is being executed by a thread.

Calling the cancel() method on the Future<V> object that represents the task sends an interrupt signal to the thread, the outcome of which depends on how the task handles the signal. In this section, we take a closer look at how cancellation can be organized programmatically when the thread that is executing a task is stopped by calling the cancel() method. See also the discussion on handling interrupt signals when a thread is interrupted by calling the Thread.interrupt() method (§22.4, p. 1393).

  • The task has completed execution.

Calling the cancel() method on the Future<V> object that represents the task has no effect on the task.

Example 23.5 illustrates task cancellation by calling the cancel() method on the Future<V> object that represents the task.

A computation-intensive task is defined by the bigFactorial() method at (1) to iteratively compute the factorial of a BigInteger. In each iteration of its for(;;) loop, the method checks at (2) whether the thread executing the method has been interrupted. If that is the case, it throws an InterruptedException. This terminates the for(;;) loop. The exception is caught and handled by the catch block at (3), causing the method to return normally. If there is no interrupt signal, the for(;;) loop continues the factorial computation. It is important that the task defines an appropriate action when the thread is interrupted.

The timedTaskCancellation() method at (4) illustrates using the timed get() method at (7) to wait for the task submitted at (6) to either finish execution and print the result at (8), or time out and throw a TimeoutException if the task did not complete execution. The output from the program shows that a TimeoutException was thrown. This exception is caught by the catch block at (9). The task is cancelled in the catch block by calling the cancel() method on the Future<BigInteger> object at (10). Since the cancel() method at (10) is called with the value true, the thread executing the task represented by the future is interrupted in an attempt to stop the task. The cancel() method returns true, indicating that the thread was interrupted. Whether the task is now cancelled is determined by the calling the isCancelled() method of the Future<BigInteger> object at (11) that returns the value true, verifying the cancellation. As only a single task is executed in the scenario above, we have used a single thread executor that is created at (5). The output from this scenario depends on the execution time to compute the factorial for the specified number at (6) and the duration of the timeout in the get() method at (7).