Java 8 introduced many important features including lambdas, stream, etc.
CompletableFuture
is also one of the most important features added in Java 8 which is useful in asynchronous programming.
If you are not aware of asynchronous programming, it is a form of parallel programming that allows a particular code block to run separately from the main thread and notifies the caller thread of its statuses like completion, failure or progress.
The main advantage of asynchronous programming is you can write non-blocking code that doesn’t block the main thread at all.
Therefore, your application will become more responsive and performance will increase.
Before we go further to create a few examples, let see what is the difference between Future
and CompletableFuture
?
Using CompletableFuture
like a simple Future
public class CompletableFuture<T> implements Future<T>, CompletionStage<T>
CompletableFuture
implements the Future
, which means that it can be used to launch a task and retrieve a result later with the get ()
method.
However, it brings a lot more functionality. It also implements the new CompletionStage
interface, which is also introduced in 1.8.
Let’s create a simple example.
package com.codedelay.concurrency; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; public class CompletableFutureDemo { private static final CompletableFuture<String> COMPLETABLE_FUTURE = new CompletableFuture<>();; public void performAsync() throws InterruptedException { Thread t1 = new Thread(() -> { try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } }); t1.start(); } public static void main(String[] args) { CompletableFutureDemo cfd = new CompletableFutureDemo(); try { cfd.performAsync(); System.out.println("get()-> " + COMPLETABLE_FUTURE.get()); System.out.println("isDone " + COMPLETABLE_FUTURE.isDone()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
In the above example, we have created an instance of CompletableFuture using no-arg constructor.
Hence, any client API who want to get the result from performAsync
can call get()
to get the result from the CompletableFuture
.
System.out.println("returnStr " + returnStr.get());
In the above example get()
method blocks until and unless the Future completes its task.
To avoid blocking the get() call forever, Let’s use completableFuture.complete();
COMPLETABLE_FUTURE.complete("Hello World!!");
completableFuture.complete ()
manually complete the Future.
To put it differently, all the client APIs who are waiting for the Future will get the result “Hello World”.
Multiple complete() call will be ignored.
COMPLETABLE_FUTURE.complete("Hello World!!"); COMPLETABLE_FUTURE.complete("Java");
The output of the above program would be:
Hello World!!
CompletableFuture can throw Exceptions
CompletableFuture can also return/throw an exception using completeExceptionally method.
COMPLETABLE_FUTURE.completeExceptionally(new RuntimeException());
completeExceptionally java.util.concurrent.ExecutionException: java.lang.RuntimeException at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:395) at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2070) at com.codedelay.concurrency.CompletableFutureDemo.main(CompletableFutureDemo.java:26) Caused by: java.lang.RuntimeException at com.codedelay.concurrency.CompletableFutureDemo.lambda$0(CompletableFutureDemo.java:18) at java.base/java.lang.Thread.run(Thread.java:835)
CompletableFuture can cancel() the execution
Suppose if you are not able to continue running performAsync
and you want to cancel the asynchronous execution, then you can also cancel the execution using the cancel()
method.
COMPLETABLE_FUTURE.cancel(false);
If we are blocked for result by calling get() method, then it will throw CancellationException
.
Exception in thread "main" true java.util.concurrent.CancellationException at java.base/java.util.concurrent.CompletableFuture.cancel(CompletableFuture.java:2475) at com.codedelay.concurrency.CompletableFutureDemo.lambda$0(CompletableFutureDemo.java:19) at java.base/java.lang.Thread.run(Thread.java:835)
Encapsulate a task using runAsync
and supplyAsync
If you want to ignore the return value and simply want to execute code asynchronously then we can use runAsync
orsupplyAsync
.
runAsync
or supplyAsync
are static methods that allow us to create a CompletableFuture instance from the Runnable and Supplier functional types accordingly.
static CompletableFuture<Void> runAsync(Runnable runnable)
runAsync
takes Runnable
and returns CompletableFuture
.
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
supplyAsync
takes Supplier
and returns CompletableFuture
.