What is the CompletableFuture API in Java?

Table of Contents

Introduction

The **CompletableFuture** API in Java, introduced in Java 8, is a powerful tool for performing asynchronous programming. It allows you to write non-blocking code, making it easier to handle parallel tasks, combine multiple asynchronous operations, and improve performance, especially in applications requiring I/O-bound or CPU-bound tasks.

In this guide, we’ll explore the **CompletableFuture** class, how it works, and how to use it for handling asynchronous operations in Java. We’ll cover key features like chaining, combining futures, and handling exceptions in asynchronous workflows.

What is CompletableFuture?

The **CompletableFuture** class is part of the **java.util.concurrent** package and represents a future result of an asynchronous computation. It allows you to write code that can perform multiple tasks concurrently, execute them asynchronously, and then combine the results when each task completes.

The core idea behind **CompletableFuture** is that it allows a task to be executed in the background without blocking the main thread. Once the task is finished, you can process the result in a callback function, chain more tasks, or handle exceptions.

Key Features of CompletableFuture

1. Asynchronous Execution

The **CompletableFuture** API lets you run tasks asynchronously without blocking the calling thread. The computation happens in the background, and once it's complete, the result can be processed using various methods provided by the API.

2. Chaining Tasks

You can chain multiple asynchronous tasks together using methods like **thenApply()**, **thenAccept()**, and **thenCombine()**. This is useful when you need to process the result of a previous task before proceeding with the next one.

3. Combining Futures

The **CompletableFuture** API provides ways to combine multiple futures into a single result using methods like **thenCombine()**, **allOf()**, and **anyOf()**. This is useful when you need to wait for multiple tasks to complete before moving forward.

4. Exception Handling

**CompletableFuture** has built-in support for handling exceptions in asynchronous tasks using methods like **handle()**, **exceptionally()**, and **whenComplete()**. This allows for graceful error handling without blocking the thread.

5. Non-blocking API

The **CompletableFuture** API allows for non-blocking execution of code, meaning it can run parallel tasks without blocking the main thread. This improves the efficiency of the program and helps in handling multiple tasks concurrently.

How to Use CompletableFuture in Java

Let’s dive into some examples of using **CompletableFuture** to handle asynchronous tasks.

1. Basic Asynchronous Task Execution

In the simplest use case, you can create a **CompletableFuture** to run a task asynchronously using the **supplyAsync()** method. This method runs the provided task in a separate thread and returns a future that can be used to get the result later.

Example: Simple Asynchronous Task

Output:

In this example, the task that returns "Hello from CompletableFuture!" is executed asynchronously. The **future.get()** method blocks the main thread until the result is available.

2. Chaining Tasks Using thenApply()

The **thenApply()** method allows you to chain another task that depends on the result of the previous asynchronous task. It processes the result asynchronously and returns a new **CompletableFuture**.

Example: Chaining Tasks

Output:

Here, the second task takes the result from the first task, multiplies it by 2, and returns the final result. Both tasks are executed asynchronously.

3. Combining Results Using thenCombine()

If you need to combine the results of two independent asynchronous tasks, you can use **thenCombine()**. This method allows you to perform an operation using the results of two different futures.

Example: Combining Futures

Output:

In this example, two independent tasks are executed, and **thenCombine()** is used to combine their results into a single value.

4. Waiting for Multiple Futures with allOf() and anyOf()

If you need to wait for multiple futures to complete before proceeding, you can use **allOf()** or **anyOf()**.

  • **allOf()** waits for all the futures to complete.
  • **anyOf()** waits for at least one future to complete.

Example: Using allOf()

Output:

In this example, both tasks run asynchronously, and **allOf()** is used to wait for both of them to complete before printing "All tasks completed".

5. Handling Exceptions with exceptionally() and handle()

You can handle exceptions that occur during the execution of an asynchronous task using **exceptionally()** or **handle()**.

  • **exceptionally()** allows you to provide a fallback value when an exception occurs.
  • **handle()** allows you to both handle exceptions and process the result.

Example: Handling Exceptions

Output:

Here, **exceptionally()** is used to catch the exception and return a default 

Conclusion

The **CompletableFuture** API is a powerful and flexible tool in Java for handling asynchronous programming. It allows you to run tasks concurrently, chain them, combine results, and handle exceptions—all without blocking the main thread. With **CompletableFuture**, you can write more efficient and scalable applications that perform better under heavy load or when dealing with I/O-bound operations.

Key takeaways:

  • **CompletableFuture** enables non-blocking, asynchronous programming.
  • You can chain and combine tasks using methods like **thenApply()**, **thenCombine()**, and **allOf()**.
  • Exception handling is built into the API, making it easy to handle errors in asynchronous workflows.
  • The API enhances concurrency management and helps you avoid blocking threads unnecessarily.

By mastering **CompletableFuture**, you can significantly improve the performance and readability of your Java applications when dealing with asynchronous operations.

Similar Questions