What is CompletableFuture in Java?
CompletableFuture in Java is a class introduced in Java 8 that provides an asynchronous, non-blocking way to compute a result and execute a callback function once the result is available. It extends the `Future` interface with additional capabilities for composition, error handling, and more flexible asynchronous programming, making it a powerful tool for developing reactive and concurrent applications.
Core Concept
At its core, a CompletableFuture represents a value that will be available at some point in the future. Unlike the traditional Future, which primarily allows checking if a computation is complete and retrieving its result (often blocking until available), CompletableFuture allows you to chain dependent actions, combine results from multiple futures, and handle errors in a non-blocking fashion. It's an excellent choice for orchestrating complex asynchronous workflows.
Key Features and Benefits
- Asynchronous Task Chaining: Allows you to define a sequence of operations that execute one after another, or in parallel, once a previous task completes, without blocking the main thread.
- Composition: Provides methods to combine multiple CompletableFutures, for example, waiting for all to complete (
allOf) or for the first one to complete (anyOf). - Non-Blocking: You attach callbacks (consumer, function) that will be executed when the future completes, instead of blocking the thread to call
get()directly. - Error Handling: Offers robust mechanisms for handling exceptions that occur during asynchronous computations.
- Manual Completion: Can be manually completed with a value or an exception, which is useful for integrating with older callback-based APIs or custom asynchronous processes.
CompletableFuture vs. Future
| Feature | java.util.concurrent.Future | java.util.concurrent.CompletableFuture |
|---|---|---|
| Composition & Chaining | Limited, typically requires blocking or manual polling. | Rich API for chaining (thenApply, thenCompose, thenCombine, etc.). |
| Callbacks | No direct support for callbacks; requires blocking `get()` or polling. | Extensive support for non-blocking callbacks. |
| Error Handling | Exceptions are thrown when `get()` is called. | Dedicated methods for exception handling (exceptionally, handle, whenComplete). |
| Completion | Can only be completed by the `Callable`/`Runnable` it's associated with. | Can be completed manually using `complete()` or `completeExceptionally()`. |
| Blocking | `get()` method is blocking. | Primarily non-blocking; `get()` is available but typically avoided. |
Basic Usage Example
Here's a simple example demonstrating how to run an asynchronous task and apply a transformation using CompletableFuture.
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
public class CompletableFutureExample {
public static void main(String[] args) {
System.out.println("Main thread started.");
// Create a CompletableFuture that runs asynchronously
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println("Task 1: Running in " + Thread.currentThread().getName());
try {
Thread.sleep(1000); // Simulate a long-running operation
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Hello";
});
// Chain another asynchronous task to transform the result
CompletableFuture<String> greetingFuture = future.thenApplyAsync(name -> {
System.out.println("Task 2: Running in " + Thread.currentThread().getName());
return name + " World!";
});
// Block and get the final result (in real applications, avoid blocking)
try {
String result = greetingFuture.get(); // Blocks until the result is available
System.out.println("Result: " + result);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Main thread finished.");
// In a real application, you might shut down the executor if it's custom.
// Default ForkJoinPool.commonPool() is used by default for supplyAsync,
// but for specific needs, you'd use CompletableFuture.supplyAsync(supplier, executor).
}
}
Common Methods
supplyAsync(Supplier<U> supplier): Runs aSupplierasynchronously and returns its result.runAsync(Runnable runnable): Runs aRunnableasynchronously.thenApply(Function<? super T,? extends U> fn): Returns a newCompletableFuturethat, when this future completes normally, is executed with this future's result as the argument to the supplied function.thenAccept(Consumer<? super T> action): Returns a newCompletableFuturethat, when this future completes normally, is executed with this future's result as the argument to the supplied action.thenRun(Runnable action): Returns a newCompletableFuturethat, when this future completes normally, executes the given action.thenCompose(Function<? super T, ? extends CompletionStage<U>> fn): FlatMap equivalent. Returns a newCompletableFuturethat is completed with the result of theCompletionStagereturned by the supplied function.thenCombine(CompletionStage<? extends U> other, BiFunction<? super T, ? super U, ? extends V> fn): Combines the results of twoCompletableFutures when both complete.allOf(CompletableFuture<?>... cfs): Returns a newCompletableFuturethat is completed when all of the givenCompletableFutures complete.anyOf(CompletableFuture<?>... cfs): Returns a newCompletableFuturethat is completed when any of the givenCompletableFutures complete.exceptionally(Function<Throwable, ? extends T> fn): Returns a newCompletableFuturethat, when this future completes exceptionally, is completed with the result of the supplied function applied to the exception.handle(BiFunction<? super T, Throwable, ? extends U> fn): Returns a newCompletableFuturethat, when this future completes (normally or exceptionally), is completed with the result of the supplied function applied to the outcome.