A Crash Course in Java Futures

Crystal Ball Not Required

James Collerton
3 min readOct 30, 2022
Let’s push things forward!

Audience

This article is for developers looking to understand Futures, and how they do asynchronous and concurrent processing. It requires a solid understanding of Java, including the purpose of threading.

We will begin with basic Futures, then move onto Completable Futures, exposing some of the functional improvements they make to their earlier incarnation.

Argument

Futures allow us to execute functionality on different threads, freeing up our current one for further computation.

Initially we require a method of retrieving different threads to execute on. This is the role of the ExecutorService. This service provides a pool of threads and the API for assigning them tasks.

If you followed the API documentation you will have noticed that ExecutorService is an interface. There are some factory methods which allow us to generate concrete implementations (demonstrated shortly), but we can also create our own.

Once we have our method of retrieving threads to run against, we need a way of defining the work we would like to do. This is the role of the Callable interface.

The callable interface exposes a single method, returning a single value. By passing a class implementing Callable to an executor service, the service knows to run the ‘call’ method and return the value in the form of a Future.

This represents the basics of Future and the ExecutorService. With Java 8 and Lambdas we can also pass anonymous classes to the service (without the need for Callable), but we won’t demonstrate that here.

With the fundamentals under our belt lets expand into CompletableFuture. This was introduced in Java8 to leverage some of its new language features.

We lead with a small example.

The strength of completable futures lies in the ability to combine them with other ‘work’ in a functional way. Here we see we have carried out the steps:

  1. Submit some functionality to a thread pool (creating a string).
  2. When we have done creating the string, kick off more work in the thread pool to add to that string.
  3. When this is done start further work to add to that string.
  4. Finally, when all this is finished print the result.

The complexity of completable future comes in the richness of the API it provides. Using further examples we will try and give a flavour of what it offers.

Here we show how we can deal with errors using the handle functionality. We can also use (non-exhaustively):

  • whenComplete: Same as handle but doesn’t return a value.
  • completeExceptionally: If the future is not already completed, calling get will throw the provided exception.
  • obtrudeException: Regardless of if the future is completed, calling get will throw the provided exception.
  • exceptionally: Here we only have access to the exception. This code block will be skipped if we haven’t thrown an error.

Another interesting thing to look at is combining futures. We can do this using functionality like acceptEither (run a function after one of two futures has finished), runAfterBoth (run a function after both of two futures have finished) and a host more.

We can also combine completable future notions with Java 8 functional ones. In the below example we combine all of the results of a list of completable futures into one. Note, this is blocking until all of them complete!

The final thing we will cover is the us of the async postfix. Some of you may have noticed that occasionally we use async (supplyAsync) and sometimes don’t (thenAccept).

Most methods have a version that uses async (thenAccept vs thenAcceptAsync). The difference is the thread pool that is used to execute the functionality. Using async you can either pass in a new Executor or it will choose another thread for you. Without it it will reuse the same thread.

Conclusion

In conclusion we have given a taste of some of the functionality included in the Java futures library, including how to use it.

--

--

James Collerton

Senior Software Engineer at Spotify, Ex-Principal Engineer at the BBC