Future.get with a timeout bounds a caller’s wait; it does not stop the task that produced the future.
Java cancellation: timed waits and cooperative interruption
Java 8+. This is a complete program using JDK classes.
Separate waiting from stopping
A quote request can time out while its worker continues contacting a supplier. Returning a timeout response is only the caller boundary. The application must decide whether to cancel, let the worker finish, or detach the result under a separately bounded policy.
cancel(true) requests interruption for a running task, but task code has to cooperate. Interruptible blocking methods can throw InterruptedException. CPU loops need their own interruption checks. A cancelled future becoming done is not proof that the worker has already released its resources.
The program uses a latch to confirm worker admission, then cancels a worker waiting on another latch. The worker records its exit in finally, and the main thread waits for that exit before printing the result. This makes shutdown observable rather than inferring it from isCancelled alone.
Pass one deadline through the stages
If admission, database work and HTTP work each start a fresh five-second timeout, one request can spend much longer than five seconds. Track a shared deadline and pass the remaining budget at each boundary. Use a monotonic time source for elapsed budgets; a wall-clock adjustment should not extend a request.
Working program
import java.util.concurrent.*;
public class CooperativeQuoteCancellation {
public static void main(String[] args) throws Exception {
ExecutorService workers = Executors.newSingleThreadExecutor();
CountDownLatch entered = new CountDownLatch(1), exited = new CountDownLatch(1);
CountDownLatch neverReleased = new CountDownLatch(1);
try {
Future<?> task = workers.submit(() -> {
entered.countDown();
try { neverReleased.await(); }
catch (InterruptedException cancelled) { Thread.currentThread().interrupt(); }
finally { exited.countDown(); }
});
if (!entered.await(2, TimeUnit.SECONDS)) throw new IllegalStateException("Not admitted");
System.out.println(task.cancel(true));
if (!exited.await(2, TimeUnit.SECONDS)) throw new IllegalStateException("Worker did not exit");
System.out.println(task.isCancelled());
System.out.println("Worker exited");
} finally {
workers.shutdownNow();
if (!workers.awaitTermination(2, TimeUnit.SECONDS)) throw new IllegalStateException("Pool did not stop");
}
}
}Output
true
true
Worker exitedCosts and boundaries
The latches use bounded waits in this program, and cleanup is checked separately from future cancellation. Real resource cleanup can require additional work, so a timeout policy must account for that phase. Cancelling a local task cannot retract a remote operation that already committed.
Common Mistakes
- TimeoutException does not automatically cancel the worker.
- Do not discard an interrupt and resume the same waiting loop.
- Future cancellation is not a remote rollback guarantee.
Read next
Completion-stage boundaries, Executor ownership.
Extend this boundary
Continue with Spring reactive foundations: request values and cancel a subscription.
Extend the tested workflow
Continue with Spring WebFlux cancellation: observe downstream cleanup without undoing work.
Compare the Python boundary
Python asyncio TaskGroup: cancel sibling work and retain failure evidence.
Continue with ownership and failure checks
Continue with Java Thread.interrupted clears the current thread's signal.
