Java 8 Concurrency API improvements

A lot of improvements were added to the concurrency features with Java 8.

  1. Classes and interfaces have been added to the java.util.concurrent package.
  2. Methods have been added to the java.util.concurrent.ConcurrentHashMap class to support aggregate operations based on the newly added streams facility and lambda expressions.
  3. Classes have been added to the java.util.concurrent.atomic package to support scalable updatable variables.
  4. Methods have been added to the java.util.concurrent.ForkJoinPool class to support a common pool.
  5. The java.util.concurrent.locks.StampedLock class has been added to provide a capability-based lock with three modes for controlling read/write access.

What is a CompletableFuture?

Java 8 introduced the CompletableFuture class. Along with the Future interface, it also implemented the CompletionStage interface. This interface defines the contract for an asynchronous computation step that we can combine with other steps.

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html

CompletableFuture is used for asynchronous programming in Java. Asynchronous programming is a means of writing non-blocking code by running a task on a separate thread than the main application thread and notifying the main thread about its progress, completion or failure. This way, your main thread does not block/wait for the completion of the task and it can execute other tasks in parallel. Having this kind of parallelism greatly improves the performance of your programs.

CompletableFuture is at the same time a building block and a framework, with about 50 different methods for composing, combining, and executing asynchronous computation steps and handling errors. Such a large API can be overwhelming, but these mostly fall in several clear and distinct use cases.

Since the CompletableFuture class implements the CompletionStage interface, we first need to understand the contract of that interface. It represents a stage of a certain computation which can be done either synchronously or asynchronously. You can think of it as just a single unit of a pipeline of computations that ultimately generates a final result of interest. This means that several CompletionStages can be chained together so that one stage’s completion triggers the execution of another stage, which in turn triggers another, and so on.

In addition to implementing the CompletionStage interface, CompletableFuture also implements Future, which represents a pending asynchronous event, with the ability to explicitly complete this Future, hence the name CompletableFuture.

Asynchronous Computation in Java

Future vs CompletableFuture

CompletableFuture is an extension to Java’s Future API which was introduced in Java 5.

A Future is used as a reference to the result of an asynchronous computation. It provides an isDone() method to check whether the computation is done or not, and a get() method to retrieve the result of the computation when it is done.

The Future interface was added in Java 5 to serve as a result of an asynchronous computation, but it did not have any methods to combine these computations or handle possible errors.

Future API was a good step towards asynchronous programming in Java but it lacked some important and useful features

Limitations of Future

  1. It cannot be manually completed
    1. Let’s say that you’ve written a function to fetch the latest price of an e-commerce product from a remote API.
    2. Since this API call is time-consuming, you’re running it in a separate thread and returning a Future from your function.
    3. Now, let’s say that If the remote API service is down, then you want to complete the Future manually by the last cached price of the product.
    4. Can you do this with Future? No!
  2. You cannot perform further action on a Future’s result without blocking
    1. Future does not notify you of its completion. It provides a get() method which blocks until the result is available.
    2. You don’t have the ability to attach a callback function to the Future and have it get called automatically when the Future’s result is available.
  3. Multiple Futures cannot be chained together
    1. Sometimes you need to execute a long-running computation and when the computation is done, you need to send its result to another long-running computation, and so on.
    2. You can not create such asynchronous workflow with Futures.
  4. You can not combine multiple Futures together
    1. Let’s say that you have 10 different Futures that you want to run in parallel and then run some function after all of them completes. You can’t do this as well with Future.
  5. No Exception Handling
    1. Future API does not have any exception handling construct.
    2. CompletableFuture implements Future and CompletionStage interfaces and provides a huge set of convenience methods for creating, chaining and combining multiple Futures. It also has a very comprehensive exception handling support.

All of these limitations are addressed with CompletableFuture.


Links to this note