Concurrency in Java: CompletableFuture and its use
- 4.0/5
- 5168
- Jul 20, 2024
The "java.util.concurrent.CompletableFuture
It is an extension to "java.util.concurrent.Future
Along with the "Future" interface, the CompletableFuture also implements the "CompletionStage" interface.
The "CompletionStage" interface defines the contract for asynchronous computation steps (functions and actions) that we can combine with other steps.
The "CompletableFuture" contains more than "fifty" different methods for composing, combining, and executing asynchronous computation steps and handling errors.
1) Run a background task asynchronously
In order to run a background task asynchronously, we can use CompletableFuture.runAsync() method. It accepts a Runnable object as input and returns a CompletableFuture
main running.. ForkJoinPool.commonPool-worker-1 ... long running job completed !!! main running..
2) Run asynchronously and return the result
The CompletableFuture.runAsync() is used to run tasks that don't return anything.
But to run a task asynchronously that returns a result, we should use CompletableFuture.supplyAsync(). It takes a Supplier
.. result returned !!!
The CompletableFuture.get() method blocks until the result is available to the main thread.
3) Using a user-defined thread-pool
By default, CompletableFuture
But it is also possible to create a thread pool and pass it to overloaded versions of the supplyAsync() and runAsync() methods.
main running.. pool-1-thread-1 ... long running job completed !!! main running..
4) Attaching a callback to the CompletableFuture
The CompletableFuture.get() method blocks until the result is available to the main thread.
But, to build an efficient asynchronous system we should be able to attach a callback to the CompletableFuture which should automatically run when the Future completes.
4.1) Using "thenApply()"
The thenApply() method can be used to process the result of a CompletableFuture when it is available.
It takes a Function
main ...running ForkJoinPool.commonPool-worker-1 ...running ForkJoinPool.commonPool-worker-1 ...running Thanks for .. returning the result !!!
4.2) Using "thenAccept()"
If we don't need to return a value further down the Future chain, we can use a Consumer
The thenAccept() method accepts a Consumer
ForkJoinPool.commonPool-worker-1 ...running ForkJoinPool.commonPool-worker-1 ...running Thanks for .. returning the result !!!
4.3) Using "thenRun()"
If we neither need the value of the computation nor want to return some value at the end of the chain, then we can use the thenRun() method.
The thenRun() method accepts a Runnable and returns an instance of the CompletableFuture
ForkJoinPool.commonPool-worker-1 ...running .. I am done !!! ForkJoinPool.commonPool-worker-1 ...running ..thanks, I am done too !!!
All the three methods discussed above have their "async" overloaded versions. These async callback variations can be used to further parallelize the computations by executing the callback tasks in a separate thread.
Furthermore, these methods have overloaded versions that accept an executor, allowing the callback to be executed in a thread from the executor's thread pool.
ForkJoinPool.commonPool-worker-1 ...running .. I am done !!! pool-1-thread-1 ...running ..thanks, I am done too !!!
5) Combining two CompletableFutures
5.1) Using "thenCompose()"
The thenCompose() method is used to combine the results of two dependent tasks when the second task is dependent on the completion of the first.
pool-1-thread-1..running pool-1-thread-1..running User-101's Account
5.2) Using "thenCombine()"
The thenCombine() method is used to combine the results of two independent tasks when the two tasks are independent of each other's completion.
pool-1-thread-1..running pool-1-thread-2..running User's Account and User's Profile
6) Combining multiple CompletableFutures
The thenCompose() and thenCombine() methods can be used to combine two CompletableFutures together.
In order to combine any number of CompletableFutures, we can use either of the following two methods.
6.1) Using "allOf()"
The allOf() static method is used to combine the results of a number of independent tasks, when "none" of the tasks are dependent on any other task.
The return type of this method is CompletableFuture
But we can get the results of all the wrapped CompletableFutures with the help of CompletableFuture.join() method and the Java 8 Streams API.
pool-1-thread-3..running pool-1-thread-1..running pool-1-thread-2..running User's Account User's Profile User's Transactions
The CompletableFuture.join() method is similar to the get() method; the only difference is that it throws an unchecked exception if the Future does not complete normally.
6.2) Using "anyOf()"
The anyOf() static method returns a new CompletableFuture<Object> as soon as any of the given CompletableFutures completes.
pool-1-thread-1..running pool-1-thread-2..running pool-1-thread-3..running I got it in c...a good developer
7) Exception Handling
7.1) Using "handle()" method
The CompletableFuture class allows us to handle exceptions in a special handle() method.
The handle() method has two parameters - the result of a computation (if successful) and the exception thrown (if the computation could not be completed normally).
In the "handle()" method, we can transform the result or recover the exception.
Invalid Username: java.lang.RuntimeException ...
7.2) Using whenComplete() callback
In the whenComplete() method, we have access to the result and exception of the current completable future as arguments, which you can consume and perform your desired action.
However, we cannot transform the current result or exception into another result.
We cannot return a value from the whenComplete() method.
Exception occurred: java.util.concurrent.CompletionException: java.lang.RuntimeException Exception: java.util.concurrent.ExecutionException: java.lang.RuntimeException
7.3) Using exceptionally() callback
In the exceptionally() method, you only have access to the exception and not the result.
If the CompletableFuture was completed successfully, then the logic inside "exceptionally()" will be skipped.
Exception occurred: java.util.concurrent.CompletionException: java.lang.RuntimeException