How to Use CompletableFuture in Java for Async Programming
Use
CompletableFuture in Java to run tasks asynchronously by creating futures with methods like runAsync or supplyAsync. You can then attach callbacks or combine futures to handle results without blocking the main thread.Syntax
CompletableFuture lets you run tasks asynchronously and handle their results later. You create a CompletableFuture using methods like runAsync for tasks without results or supplyAsync for tasks that return a value. You can then use methods like thenApply, thenAccept, or thenCompose to process the results.
CompletableFuture.runAsync(Runnable task): Runs a task without returning a result.CompletableFuture.supplyAsync(Supplier supplier): Runs a task that returns a result.thenApply(Function: Transforms the result when ready.fn) thenAccept(Consumer: Consumes the result without returning anything.action) thenCompose(Function: Chains futures.> fn)
java
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
// code to run asynchronously
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
// code that returns a String result
return "Hello";
});
future2.thenApply(result -> result + " World")
.thenAccept(System.out::println);Example
This example shows how to run a task asynchronously that returns a value, then transform and print the result without blocking the main thread.
java
import java.util.concurrent.CompletableFuture; public class CompletableFutureExample { public static void main(String[] args) { CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { // Simulate a long-running task try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return "Hello"; }); future.thenApply(result -> result + " World") .thenAccept(System.out::println); System.out.println("Task started"); // Wait for the async task to finish to see output future.join(); } }
Output
Task started
Hello World
Common Pitfalls
Common mistakes when using CompletableFuture include:
- Not waiting for the future to complete, causing the program to exit before async tasks finish.
- Blocking the main thread unnecessarily, which defeats the purpose of async programming.
- Ignoring exceptions thrown in async tasks, which can cause silent failures.
Always handle exceptions with exceptionally or handle, and use join() or get() to wait if needed.
java
import java.util.concurrent.CompletableFuture; public class PitfallExample { public static void main(String[] args) { // Wrong: Not waiting for completion CompletableFuture.runAsync(() -> { try { Thread.sleep(500); System.out.println("Async task done"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); System.out.println("Main thread ends"); // Right: Wait for completion CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { try { Thread.sleep(500); System.out.println("Async task done"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); future.join(); System.out.println("Main thread ends after async task"); } }
Output
Main thread ends
Async task done
Async task done
Main thread ends after async task
Quick Reference
| Method | Description |
|---|---|
| runAsync(Runnable) | Run a task asynchronously without returning a result. |
| supplyAsync(Supplier | Run a task asynchronously that returns a result. |
| thenApply(Function | Transform the result when ready. |
| thenAccept(Consumer | Consume the result without returning anything. |
| thenCompose(Function | Chain dependent futures. |
| exceptionally(Function | Handle exceptions and provide fallback. |
| join() | Wait for completion and get the result or throw unchecked exception. |
Key Takeaways
Use CompletableFuture to run tasks asynchronously without blocking the main thread.
Create futures with runAsync for void tasks and supplyAsync for tasks returning results.
Chain and transform results using thenApply, thenAccept, and thenCompose.
Always handle exceptions in async tasks to avoid silent failures.
Use join() or get() to wait for completion when necessary.