To cancel long-running tasks in an ExecutorService
, you can use the Future
object returned when you submit a task and invoke its cancel
method. Below are the steps and some important considerations for canceling tasks:
1. Submit Tasks to the ExecutorService
When you submit a task to an ExecutorService
, it returns a Future
object that can be used to monitor the task’s progress and cancel it if needed.
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<?> future = executor.submit(() -> {
// Simulate a long-running task
try {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Running...");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore the interrupted status
System.out.println("Task interrupted.");
}
});
2. Cancel the Task
To cancel the task, invoke the cancel
method on the Future
object:
// Cancel the task after 5 seconds
Thread.sleep(5000); // Simulating some delay
boolean wasCancelled = future.cancel(true); // true means interrupt if running
System.out.println("Task cancelled: " + wasCancelled);
cancel(true)
attempts to stop the execution of the task by interrupting the thread running it. For this to work, the task must regularly check its interrupted status (viaThread.interrupted()
orThread.currentThread().isInterrupted()
) and gracefully terminate if interrupted.cancel(false)
does not interrupt the running task but prevents it from starting if it hasn’t already begun.
3. Handle Interruption Gracefully
For the cancellation to work, ensure that the task checks the interrupted status and responds accordingly. The task should periodically call Thread.interrupted()
or Thread.currentThread().isInterrupted()
to detect interruptions.
try {
while (!Thread.currentThread().isInterrupted()) {
// Simulate work
System.out.println("Working...");
Thread.sleep(1000); // This can throw InterruptedException
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Re-set the interrupted status
System.out.println("Task interrupted and exiting.");
}
4. Shutdown the ExecutorService
Once you’re done submitting tasks, shut down the ExecutorService
to release resources:
executor.shutdown(); // Wait for running tasks to complete
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow(); // Forcefully shut down if tasks don't finish in time
}
} catch (InterruptedException e) {
executor.shutdownNow(); // Force an immediate shutdown
Thread.currentThread().interrupt(); // Reset the interrupted status
}
Keynotes
- Interruptible Tasks: The tasks you submit must be designed to handle interruptions for cancellation to effectively work. For example, long-running loops or blocking calls should handle the interrupted status.
- Blocking Methods: If the task is waiting on a blocking call (e.g.,
Thread.sleep()
,Object.wait()
,Future.get()
), callingcancel(true)
will usually interrupt these methods. - Non-Interruptible Work: If the task is not interruptible (e.g., performing intensive computations without checking the interrupted flag),
cancel(true)
will not have an immediate effect. - Future API: You can also check the status of a task using methods like
isDone()
,isCancelled()
before or after attempting to cancel it.
This approach ensures your long-running task can be terminated gracefully and resourcefully.