Java CompletableFuture Tutorial with Async Examples
CompletableFuture
is a powerful feature in Java that simplifies writing asynchronous and non-blocking code. Introduced in Java 8 as part of the java.util.concurrent
package, it enables developers to model computations that will complete in the future and chain actions based on their outcomes. With methods like supplyAsync()
, thenApply()
, thenCombine()
, and exceptionally()
, CompletableFuture
supports building complex async pipelines with ease. This tutorial provides practical examples of how to use CompletableFuture
for asynchronous task execution, error handling, result composition, and synchronization in modern Java applications.You can use CompletableFuture
to represent a future result of an asynchronous computation and perform various operations on the result. Here are some common usage patterns with code examples:
1. Creating a CompletableFuture:
You can create a CompletableFuture
and provide a computation that will be executed asynchronously. You can use supplyAsync
to run a function and return a result:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 42);
}
}
2. Chaining Operations:
You can chain operations on a CompletableFuture
using methods like thenApply
, thenCompose
, and thenAccept
:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 21)
.thenApply(result -> result * 2)
.thenAccept(result -> System.out.println("Final result: " + result));
}
}
3. Handling Errors:
You can handle exceptions with the exceptionally
method:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 10 / 0)
.exceptionally(ex -> {
System.err.println("An error occurred: " + ex.getMessage());
return 0;
});
}
}
4. Combining Multiple CompletableFutures:
You can combine the results of multiple CompletableFuture
instances using methods like thenCombine
and thenCompose
:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static void main(String[] args) {
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 21);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 21);
CompletableFuture<Integer> combined = future1.thenCombine(future2, (result1, result2) -> result1 + result2);
}
}
5. Waiting for Completion:
You can block and wait for a CompletableFuture
to complete using the get
method. Be cautious when using get
, as it can block the current thread:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 42);
try {
Integer result = future.get(); // This blocks until the result is available
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
6. Combining Multiple Futures:
You can wait for multiple CompletableFuture
instances to complete using CompletableFuture.allOf
:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static void main(String[] args) {
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 21);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 42);
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(future1, future2);
try {
combinedFuture.get(); // This blocks until both futures are completed
} catch (Exception e) {
e.printStackTrace();
}
}
}
CompletableFuture
is a powerful tool for working with asynchronous tasks and is commonly used in Java applications to improve concurrency and responsiveness. It provides a flexible and composable way to work with asynchronous operations.
CompletableFuture
brings modern, functional-style asynchronous programming to Java. It allows you to write clean, non-blocking code with better control over execution flow, error recovery, and task composition. Whether you're building responsive UIs or scalable backend services, CompletableFuture
is a vital tool in your Java concurrency toolkit.
FAQ Section
1. What is CompletableFuture in Java?
CompletableFuture
is an async computation that completes in the future, allowing non-blocking, chained, and combinable operations.
2. How does CompletableFuture differ from Future?
Unlike Future
, CompletableFuture
supports chaining, exception handling, composition, and non-blocking programming.
3. What is supplyAsync used for?
supplyAsync()
starts an async task that returns a value, typically run on a common ForkJoinPool.
4. How do you handle exceptions in CompletableFuture?
Use exceptionally()
, handle()
, or whenComplete()
to recover from exceptions during execution.
5. Can you combine multiple CompletableFutures?
Yes, methods like thenCombine()
, thenCompose()
, and allOf()
allow composition of multiple futures.
6. What happens if get() is called before the future is complete?
The current thread will block and wait until the future is completed or an exception is thrown.
7. Is CompletableFuture thread-safe?
Yes, it is thread-safe and designed for use in concurrent environments.
8. Can I cancel a CompletableFuture?
Yes. You can call future.cancel(true)
, but cancellation is cooperative and depends on the task’s behavior.
Leave a Comment