CompletableFuture Creation and Completion
In Java, a `CompletableFuture` can represent work that has not finished yet, be completed directly with a value or exception, or be created from asynchronous supplier and runnable tasks. The main creation methods differ in whether they perform work and whether they produce a result.
A `CompletableFuture<T>` is both a `Future` representing an eventual result and a `CompletionStage` that can participate in dependent asynchronous operations. You can create an incomplete instance with `new CompletableFuture<>()` when some other part of the program will complete it later.
For an already-known result, `CompletableFuture.completedFuture(value)` creates a future that is immediately complete. To create asynchronous work that returns a value, `CompletableFuture.supplyAsync(supplier)` runs a `Supplier<T>` asynchronously and completes the future with its returned value. To run asynchronous work that does not return a value, `CompletableFuture.runAsync(runnable)` accepts a `Runnable` and produces a `CompletableFuture<Void>`.
An explicitly created future can be finished with `complete(value)`. Calling `completeExceptionally(exception)` finishes it with an exceptional outcome. Completion methods return `true` when they successfully complete the future and `false` when it was already completed.
By default, the `Async` factory methods use the common `ForkJoinPool`, unless an explicit `Executor` overload is supplied. For backend applications, choosing an appropriate executor can matter when work is blocking, requires isolation, or needs controlled concurrency.
Key points
- `new CompletableFuture<>()` creates an incomplete future that can be completed later.
- `completedFuture(value)` creates an already-successful future.
- `supplyAsync` performs asynchronous work and produces a result.
- `runAsync` performs asynchronous work without a result.
- `complete` and `completeExceptionally` explicitly finish an incomplete future.
Interview questions
How do you create a `CompletableFuture` that is already completed?
Use `CompletableFuture.completedFuture(value)`. It returns a future whose successful result is already available. This is useful when an API consistently returns `CompletableFuture` but the result is already known. No asynchronous task is started by this factory method.
What is the difference between `supplyAsync` and `runAsync`?
Both start asynchronous work, but they model different task shapes. `supplyAsync` takes a `Supplier<T>` and completes its future with the supplier's returned value. `runAsync` takes a `Runnable`, so its future has type `CompletableFuture<Void>` and represents completion rather than a computed result.
Why would you use `new CompletableFuture<>()` instead of `completedFuture()`?
Use `new CompletableFuture<>()` when the result will become available later and some code needs to complete the future explicitly. The new instance starts incomplete, so another operation can call `complete` or `completeExceptionally`. `completedFuture` is appropriate only when the successful value is already known at creation time.
What happens when `complete` is called on an already completed `CompletableFuture`?
The original completion remains unchanged. The second call returns `false`, indicating that this call did not complete the future. The first successful completion determines the future's outcome, whether that outcome was a normal value or an exception.
What executor is used by `supplyAsync` by default, and why can that matter?
Without an executor argument, `supplyAsync` uses the default asynchronous execution facility, which is the common `ForkJoinPool`. This can be unsuitable for blocking operations because blocking tasks can interfere with other work sharing that pool. Applications may instead provide an executor designed for the workload. The executor overload also makes concurrency and resource usage more explicit.