Card 06/ 09
Future vs CompletableFuture
Waiting for a result, or describing what happens when it arrives.
A Future gives you one thing: a way to wait for a result. A CompletableFuture is a Future you can also attach steps to, saying what should happen when the result arrives, without a thread standing there waiting for it. The table compares what each lets you do with a pending result.
| Compared on | Future | CompletableFuture |
|---|---|---|
| Get the result | get(), which blocks | join(), or a callback that does not block |
| Do something when it arrives | Nothing: you must wait | thenApply, thenAccept, thenCompose |
| Combine two results | Wait for both in turn | thenCombine |
| Handle a failure | Catch around get() | exceptionally, handle |
| Complete it yourself | No | complete(value) |
CompletableFuture<Long> rows = CompletableFuture.supplyAsync(() -> countRowsIn(file), pool);
rows.thenApply(n -> n * 2)
.thenAccept(n -> log.info("{} rows", n))
.exceptionally(e -> { log.error("failed", e); return null; });Nothing blocks. Each stage says what to do when the previous one finishes, and the whole chain runs on the pool as results arrive: the same shape as a stream pipeline, over time instead of over elements.
The decision rule. A Future is enough when you submit a handful of tasks and wait for all of them. Use CompletableFuture when one result feeds another call, when two results have to be combined, or when there is no thread you can afford to block.
← → move through sections and cards