Difference Between CompletableFuture And Future In Java

Reetesh Kumar
3 min readFeb 1, 2024

--

Introduction

Java, as a programming language, has undergone several changes and enhancements over the years, especially in how it handles asynchronous programming. Two key components in this domain are Future and CompletableFuture. This blog post aims to delve into compare their features, and understand their use cases in Java.

Basic Characteristics

Future: Introduced in Java 5, Future represents the result of an asynchronous computation. It provides methods to check if the computation is done, to wait for its completion, and to retrieve the result. However, its capabilities are quite basic and limited. Read More On Future.

CompletableFuture: Introduced in Java 8, CompletableFuture is an enhancement of Future. It not only represents a future result but also provides a plethora of methods to compose, combine, execute asynchronous tasks, and handle their results without blocking. Read More On CompletableFuture.

Method Of Use

Future: It is generally used with an ExecutorService, and we get a Future object when we submit tasks to the executor. However, it doesn’t allow us to define any computation steps to be executed once the computation is finished. The get() method is blocking, which means it waits until the task is completed and can make the application less responsive.

CompletableFuture: It can be used as a Future and also allows us to attach callbacks via methods like thenApply, thenAccept, and thenRun. These methods let us execute additional actions upon completion of the original task, in a non-blocking fashion. CompletableFuture can also be manually completed.

Exception Handling

Future: Does not provide a built-in mechanism for handling exceptions. Exception handling needs to be implemented externally. Exceptions are caught during the get() method call.

CompletableFuture: Provides methods like exceptionally and handle to deal with exceptions in a chain of asynchronous tasks. Allows defining a recovery or fallback mechanism within the computation chain.

Composing and Combining Results

Future: Lacks native support for combining multiple Future instances or creating a sequence of asynchronous operations.

CompletableFuture: Supports combining multiple CompletableFuture instances using methods like thenCompose, thenCombine, and allOf. Facilitates the composition and sequencing of multiple asynchronous computations.

Blocking vs. Non-Blocking

Future: Primarily relies on blocking operations (get()) for retrieving the result of the asynchronous computation.

CompletableFuture: Encourages non-blocking programming. It allows us to process results with functions and actions that get applied asynchronously, thus enhancing the responsiveness of your application.

Asynchronous Execution Support

Future: Does not inherently support the execution of asynchronous code. It’s typically used with ExecutorService to execute Runnable or Callable tasks.

CompletableFuture: Offers built-in support for asynchronous execution with methods like supplyAsync, runAsync, and variants with custom Executor.

Use Cases

Future: Suitable for simple asynchronous operations where we need to perform tasks in a separate thread and retrieve the results later.

CompletableFuture: Ideal for complex asynchronous programming needs, including chaining multiple asynchronous operations, error handling, combining results, and implementing non-blocking algorithms.

Conclusion

In summary, while Future provides a basic structure for handling asynchronous computation, CompletableFuture offers a much more robust and flexible framework. It caters to complex asynchronous programming paradigms, making it a preferred choice for modern Java applications dealing with concurrent tasks and asynchronous programming.

Happy Learning !!!

--

--

Reetesh Kumar

Software developer. Writing about Java, Spring, Cloud and new technologies. LinkedIn: www.linkedin.com/in/reeteshkumar1