How do you retrieve the result of an asynchronous task with Future, and what are the semantics of get(), the timed get(), isDone(), isCancelled(), and cancel()?
answer
- get() blocks; timed get() throws TimeoutException
- Task threw -> ExecutionException.getCause()
- Waiter interrupted -> InterruptedException
- isDone() true for normal/exception/cancel — not success
- cancel(true) interrupts; only stops interruptible code
basics
~20 sFuture is a handle to a task's eventual result. future.get() blocks until the task finishes and returns the value (or throws if it failed). The timed get() waits at most a duration. isDone()/isCancelled() check state; cancel() tries to stop it.
solid answer
~50 sSubmitting a Callable returns a Future<V> — a placeholder for a result that isn't ready yet. get() blocks the calling thread until the task completes, then returns the value; if the task threw, get() throws ExecutionException wrapping that cause; if the waiting thread is interrupted it throws InterruptedException; if the task was cancelled it throws CancellationException. The timed get(timeout, unit) is the same but throws TimeoutException if the result isn't ready in time — crucial to avoid waiting forever. isDone() returns true once the task finished for any reason (normal, exception, or cancellation). isCancelled() is true only if it was cancelled. cancel(mayInterruptIfRunning): if the task hasn't started it won't run; if it's running and mayInterruptIfRunning is true the worker thread is interrupted — but the task only actually stops if its code is responsive to interruption. cancel returns false if the task already completed.
go deeper
Knows get() blocks and returns the result, and that the timed get() exists to avoid waiting forever.
Articulates all of get()'s exceptions (InterruptedException, ExecutionException, TimeoutException, CancellationException), unwraps getCause(), and explains isDone vs isCancelled.
Explains cooperative cancellation precisely (interrupt is a request; only interruptible/checking code stops) and why TimeoutException doesn't cancel; chooses timed get() defensively.
Discusses Future's limitations (no non-blocking composition, no callbacks) motivating CompletableFuture, and interrupt-policy design across a service's task code.
## What a Future is A **`Future<V>`** is a *handle* to a result that will exist *in the future*. When you hand a task to a thread pool, the pool runs it on **another** thread, so the result isn't available at the moment of submission. Instead of blocking immediately, `submit` returns a `Future<V>` you can query or wait on later. ```java ExecutorService pool = Executors.newFixedThreadPool(2); Future<Integer> f = pool.submit(() -> slowSum()); // returns immediately // ... do other work ... int result = f.get(); // now wait for it ``` ## get() — the blocking retrieval `V get()` **blocks** (parks the calling thread) until the task finishes, then returns its value. Its checked exceptions: - **`InterruptedException`** — the *thread that is waiting* in `get()` was interrupted. (This is about the waiter, not the task.) - **`ExecutionException`** — the task itself threw. The original throwable is available via `getCause()`. The framework wraps it so a thrown checked exception can surface through `get()`'s fixed signature. And one unchecked exception: - **`CancellationException`** — the task was cancelled before producing a result. If the task already completed, `get()` returns (or throws) immediately — the result is cached, so calling `get()` multiple times is fine. ## get(timeout, unit) — bounded waiting ```java try { int r = f.get(2, TimeUnit.SECONDS); } catch (TimeoutException e) { // result not ready in 2s — decide what to do } ``` Same as `get()` but throws **`TimeoutException`** if the deadline passes first. A `TimeoutException` does **not** cancel the task — it just stops *you* waiting; the task keeps running. Always prefer the timed form in production so a hung task can't block a caller forever. ## State queries - **`isDone()`** — true once the task is finished *for any reason*: completed normally, threw, or was cancelled. It does **not** tell you *how* it finished, and `isDone() == true` does **not** mean success. - **`isCancelled()`** — true only if the task was cancelled before completing normally. Polling `isDone()` in a busy loop is wasteful; usually you just call `get()` (or a timed `get()`), or use a `CompletionService` (below). ## cancel(mayInterruptIfRunning) ```java boolean cancelled = f.cancel(true); ``` - If the task **hasn't started**, it is removed from the queue and will never run; `cancel` returns `true`. - If the task is **already running**: - `mayInterruptIfRunning == true` → the executing thread receives an **interrupt** (its interrupt flag is set / a blocking call throws `InterruptedException`). The task **only actually stops if its code checks `Thread.interrupted()` or calls interruptible blocking methods**. CPU-bound code that ignores interrupts keeps running. - `mayInterruptIfRunning == false` → it lets a running task finish; it only prevents a not-yet-started task from running. - If the task **already completed**, `cancel` returns `false` and nothing happens. After a successful cancel, `isCancelled()` and `isDone()` are both true, and `get()` throws `CancellationException`. ## Key takeaways - `get()` is **blocking**; the timed variant gives you a safety valve. - `ExecutionException.getCause()` is where the real failure lives. - Cancellation is **cooperative** — interruption is a request, not a kill.
- Your task threw an IllegalStateException. What exactly does future.get() throw, and how do you reach the original exception?get() throws ExecutionException; the IllegalStateException is available via executionException.getCause().
- Does get(1, SECONDS) throwing TimeoutException stop the underlying task?No. The task keeps running. TimeoutException only ends your wait. To stop it you must call cancel(true), and even then only interruptible task code will actually halt.
saying these in an interview costs you the question
- Thinking isDone() == true means the task succeeded.
- Assuming cancel(true) forcibly kills a running thread — it only sends an interrupt.
- Believing TimeoutException cancels the task.
- Catching ExecutionException and logging it without unwrapping getCause().
- Calling get() with no timeout in code that must stay responsive.