Future vs. CompletableFuture — #1

Knoldus Inc.
Knoldus - Technical Insights
4 min readJan 20, 2018

This is Part 1 of Future vs. CompletableFuture. In this blog we will be comparing Java 5’s Future with Java 8’s CompletableFuture on the basis of two categories i.e. manual completion and attaching a callable method.

What is CompletableFuture?

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.

CompletableFuture implements two interfaces:

  1. Future
  2. CompletionStage

Future vs. CompletableFuture

1. Manual Completion

Talking about manual completition , future 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. But if there comes a scenario where you need to complete it manually, Future does not provides any means to do so.

But in Java 8’s completableFuture, CompletableFuture.complete() method helps us to manually complete a Future. Let’s have a look at an example to see it in more detail.

Creating a CompletableFuture simple by having a no-arg constructor.

[code language=”java”]
CompletableFuture<String> completableFuture = new CompletableFuture<String>();
[/code]

To fetch the result, you can use get() method.

[code language=”java”]

String result = completableFuture.get();

[/code]

Since we know that get() method blocks until the future is complete, the above call will block forever since the future is never completed. Therefore, we can use complete() method in order to manually complete the result.

[code language=”java”]

completableFuture.complete(“Welcome to Knoldus!”);

[/code]

2. Attaching a callable method

While using Future, we do not get notified when it is complete neither does it provides us a callable method which will automatically be called when the result is available but CompletableFuture provides us with a lot of callable methods which can be used as per our use case. But before we go through callable methods let’s have a basic understanding of runAsync() and supplyAsync() for asynchronous computation.

a) runAsync()

It is used for running some background task asynchronously but not returning anything by using a Runnable instance. It takes a Runnable object and returns CompletableFuture<Void>. For example:

[code language=”java”]
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
System.out.println(“Running asynchronous task in parallel”);
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException ex) {
throw new IllegalStateException(ex);
}
});
[/code]

In the above example, we have used lambda expression for passing Runnable object.

b) supplyAsync()

This method is used when you want to return some value from the background task running asynchronously. It takes a Supplier<T> and returns CompletableFuture<T>. Here, Supplier<T> is a functional interface and T is the type of value returned from the supplier. For example:

[code language=”java”]
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return “This is the result of the asynchronous computation”;
});
[/code]

Note: Async methods are used to run the task on a separate thread apart from the main thread whereas the methods without the Async postfix run the execution stage using a calling thread. When using methods with Async postfix, you can specify the executor as second argument. By default it is ForkJoinPool.commonPool() method that is used.

Now, let’s have a look at each of the callable method:

i) thenApply()

It takes a Function<T,R> as an argument. Function<T,R> is a functional interface which represents a function that it takes argument of type T and returns argument of type R.

[code language=”java”]
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return “Knolders!”;
});

CompletableFuture<String> result = completableFuture.thenApply(name -> {
return “Hello “ + name;
});
[/code]

We can even apply a sequence of transformations using thenApply() where the result of 1st thenApply() is passed to the 2nd thenApply() and so on. For example:

[code language=”java”]
CompletableFuture<String> result = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return “Knolders!”;
}).thenApply(name -> {
return “Hello “ + name;
}).thenApply(greeting -> {
return greeting + “ Welcome to Knoldus Inc!”;
});

System.out.println(result.get()); // Hello Knolders! Welcome to Knoldus Inc!
[/code]

ii) thenAccept()

It takes a Consumer<T> and returns CompletableFuture<Void>. It has access to the result of the CompletableFuture on which it is attached. For example:

[code language=”java”]
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> “Knolders!”);
CompletableFuture<Void> result
= completableFuture.thenAccept(value -> System.out.println(“Hello “ + value));
[/code]

iii) thenRun()

It also takes a Consumer<T> and returns CompletableFuture<Void>. If you neither need the value of the computation nor want to return some value at the end of the chain, then you can pass a Runnable lambda to the thenRun() method. Hence, in this case we do not have access to future’s result.

For example:

[code language=”java”]
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> “Knolders!”);
CompletableFuture<Void> result
= completableFuture.thenRun(() -> System.out.println(“Example with thenRun().”));
[/code]

Note:

  • While processing results of asynchronous computations, we also have callable methods with async variants where we can specify the executor as discussed above.
  • The consumer methods are often used as the last callback in the callback chain.

Upcomings

In the next blog, I will be comparing Future with CompletableFuture on the basis of

  • Combining 2 CompletableFutures together
  • Combining multiple CompletableFutures together
  • Exception Handling

References

knoldus-advt-sticker

--

--

Knoldus Inc.
Knoldus - Technical Insights

Group of smart Engineers with a Product mindset who partner with your business to drive competitive advantage | www.knoldus.com