Free tools Windows power users keep installed
One-click scans. No signup required.
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
You do not wait for FutureTask.cancel() itself: it is a synchronous call. To wait until the FutureTask reaches a terminal state, call get() and handle the expected CancellationException. That confirms the future is canceled; it does not necessarily mean the task’s code has stopped running. cancel(true) requests interruption, which the task must cooperate with.
The basic cancel-and-wait pattern
For an unbounded wait for the future’s state, cancel it and then call get():
task.cancel(true);
try {
task.get();
} catch (CancellationException expected) {
// The FutureTask reached its canceled state.
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// This waiting thread was interrupted.
} catch (ExecutionException e) {
// The computation failed rather than completing through cancellation.
}
A canceled future does not return a value from get(); it reports cancellation by throwing CancellationException. If cancellation loses a race with successful completion, get() can return the result instead. If the computation failed first, it throws ExecutionException.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11The key distinction is between FutureTask completion and the end of the task body. A successful cancel(true) can mark the future canceled and request an interrupt while the callable is still running cleanup—or continues to run because it ignores interruption. Consequently, get() after cancellation is not proof that arbitrary task code has physically stopped.
#1 Best Overall
What cancel(false) and cancel(true) mean
cancel(false)attempts to prevent a task that has not started from running. If it is already running, no interrupt is requested, so its code may continue even if the future becomes canceled.cancel(true)also attempts to interrupt the thread executing the task, if it is running. Interruption is a request, not forced termination. A task that ignores the interrupt, suppressesInterruptedException, or is stuck in a non-interruptible operation may continue.
The boolean returned by cancel reports whether that cancellation attempt succeeded. If the task has already completed or another cancellation won the race, the call may return false. Check isCancelled() when you need the future’s cancellation status; do not infer the final state solely from the return value at one call site. The FutureTask API documents these methods and their completion behavior.
Why get() is better than polling
get() is the blocking wait supplied by the Future abstraction. By contrast, isDone() is a nonblocking status check: it returns true after normal completion, exceptional completion, or cancellation. It does not tell you that a result is available or that cancellation specifically occurred.
// Usually avoid this as a waiting strategy:
while (!task.isDone()) {
Thread.sleep(10);
}
Polling adds arbitrary latency and wakeups, requires its own interruption handling, and still requires checking how the future completed. Use get() for an indefinite wait, or timed get when the caller has a deadline. Do not use a fixed sleep as a synchronization protocol.
Handle interruption in both threads
There can be two separate interrupt concerns: the worker receiving the cancellation request, and the caller waiting in get(). If the waiting thread is interrupted, get() throws InterruptedException. If the method cannot propagate that exception, restore the interrupt status with Thread.currentThread().interrupt(), as in the example above; do not silently swallow it.
Rank #2
The task should also cooperate with interruption. For example, a loop can check the interrupt flag and release resources in a finally block:
FutureTask<Void> task = new FutureTask<>(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {
doSmallUnitOfWork();
}
} finally {
releaseResources();
}
return null;
});
For interruptible blocking calls, handle InterruptedException by exiting or propagating the cancellation signal. If you catch it and continue without a deliberate policy, the task may keep running after its future has been canceled:
try {
blockingOperation();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
Java interruption is cooperative; it does not safely kill a thread. Do not use deprecated forced-stop techniques as a substitute for designing a task that can stop and clean up.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →If you need proof that the task body stopped
Have the task publish its own termination acknowledgment, normally from a finally block. A latch lets the caller separately wait for that signal:
CountDownLatch stopped = new CountDownLatch(1);
FutureTask<Void> task = new FutureTask<>(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {
doWork();
}
} finally {
stopped.countDown();
}
return null;
});
executor.execute(task);
// Later:
task.cancel(true);
try {
task.get();
} catch (CancellationException expected) {
// FutureTask cancellation observed.
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
if (!stopped.await(5, TimeUnit.SECONDS)) {
throw new TimeoutException("Task did not acknowledge cancellation");
}
Import CountDownLatch, TimeUnit, and the relevant exception types from java.util.concurrent. The latch must be released in a finally block, and the task must reach that block. If it ignores interruption or remains stuck, the acknowledgment may never arrive; the timeout detects that condition but cannot force the task to stop.
If your application directly owns the worker Thread, join() can wait for that specific thread’s termination after requesting cancellation. This is not a general way to get the worker thread from an arbitrary executor. For a whole executor, use its shutdown and termination APIs rather than treating one future as a proxy for every worker.
Timed waits and cancellation races
Use get(timeout, unit) to bound how long the caller waits:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →task.cancel(true);
try {
task.get(5, TimeUnit.SECONDS);
} catch (CancellationException expected) {
// The future is canceled.
} catch (TimeoutException e) {
// The caller's wait expired; this did not stop the task.
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
// The computation failed.
}
A timeout means only that no terminal result was observed within the allotted wait. It does not cancel the task. If your policy is to request cancellation after a wait expires, call cancel(true) explicitly—and remember that interruption still depends on task cooperation.
Completion and cancellation can race. The task may finish normally or exceptionally just before cancellation takes effect; another caller may cancel it first. Treat the future’s state and get() outcome as authoritative:
- If cancellation wins,
isCancelled()is true andget()throwsCancellationException. - If normal completion wins,
get()returns the result. - If exceptional completion wins,
get()throwsExecutionException. isDone()is true in all three terminal cases, so it does not identify which occurred.
For the full contract, see the Java 26 Future API and the FutureTask API.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.done() is not task-body acknowledgment
A FutureTask subclass can override done() for notification or bookkeeping:
@Override
protected void done() {
completionSignal.countDown();
}
That hook corresponds to the FutureTask entering its done state, including cancellation. It should not be treated as proof that user code has ceased executing after an interrupt-based cancellation. Put a task-body acknowledgment in the callable’s finally block when that stronger guarantee matters.
Best Value
Using an executor or a different future type
For ordinary application code, ExecutorService.submit(...) returns a Future; program to that interface unless you specifically need FutureTask features. The same basic cancellation and waiting pattern applies:
Future<Result> future = executor.submit(callable);
future.cancel(true);
try {
Result result = future.get();
} catch (CancellationException expected) {
// Canceled.
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
// Failed.
}
If you need to wait for the entire executor to stop accepting and finish work, use executor lifecycle methods:
executor.shutdown();
if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
executor.shutdownNow();
if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
throw new IllegalStateException("Executor did not terminate");
}
}
shutdownNow() requests interruption of active tasks; it does not guarantee termination for tasks that ignore interrupts. This is a different scope from waiting for one future.
CompletableFuture has a different cancellation model: cancellation completes that future exceptionally but does not provide the same direct control over the computation that might have produced its result. Do not substitute it when the requirement is specifically to interrupt a running task. A completed FutureTask is not normally reusable for another computation; create a new task instead.
Quick Recap
Quick decision guide
| What you need | Use | Limitation |
|---|---|---|
| Wait for the FutureTask’s terminal state | get(), catching CancellationException |
Does not prove task code has stopped |
| Request cooperative stop of running work | cancel(true) and interruption-aware task code |
Task must honor interruption |
| Bound how long the caller waits | get(timeout, unit) |
Timeout does not cancel the task |
| Wait for task cleanup or body exit | Task-owned latch or completion signal from finally |
Signal may time out if the task does not exit |
| Wait for one directly managed thread | Thread.join() |
Requires ownership of that thread |
| Wait for executor termination | awaitTermination(...) |
Applies to the executor, not just one task |
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

