The Completable Future
Hello guys, in this blog we are going to talk about the CompletableFuture which was introduced in Java 8. But before looking into it, let me give you some background of the Future.
Futures were introduced in Java 5. They are basically placeholders for a result of an operation that hasn’t finished yet. Once the operation finishes, the Future will contain the result. For example, an operation can be a Runnable or Callable instance that is submitted to an ExecutorService. The submitter of the operation can use the Future object to check whether the operation isDone(), or wait for it to finish using the blocking get() method. I have added a few examples in one of my blogs. Here is the link below.
The Thread and The Executor Framework
In this blog, we will discuss about the difference between a thread and a process, executor framework and Java 8’s new…
CompletableFutures were introduced in Java 8. They are an evolution of regular Futures. The CompletableFuture class implements Future and CompletionStage interfaces. The Future interface helps you to get the value from the thread whereas the CompletionStage interface allows you to chain tasks, that is, execute tasks one after the other. This combined power of both interfaces helps you to write callbacks and execute code asynchronously. Let us look into an example.
If you see the above example, I have used the runAsync method which takes a runnable task (Returns void) as an input, and then I have chained it with the callback. The callback will be executed when the task supplied to the runAsync gets over. Now let us understand how runAsync() method works.
By default, the runAsync method internally makes use of the common ForkJoinPool. See the output of the above program.
To know more about ForkJoin Framework and of course the ForkJoinPool, read the blog given below
The Stealer — Fork Join Framework
In this blog, we will discuss Fork Join Framework in depth and how it can create a problem in specific scenarios.
But, you can override the default behavior by making use of the overloaded runAsync() method. This overloaded method will allow you to create your own thread pool and then pass on the task to that pool. If your task is a time consuming one or it might get blocked for some reason such as I/O operation, it is better to create your own thread pool.
Let us see how we can create our own custom thread pool with the help of an example.
In the above program, I have purposely created ForkJoinPool using newWorkSteelingPool, just to show that you can create your own. But, you are free to use any of the implementations of the ExecutorService as per your requirement. Here is the output of the above program.
The CompletableFuture also provides a method named supplyAsync() which can be used to return the value.
In this case, I have supplied a task to supplyAsync() method which returns a string value, and then it is consumed by the callback. Here, I have also created a fixed-size thread pool, just to show that you can use any of the ExecutorService implementation based on your needs.
Now, let us see how we can get a value from a CompletableFuture object.
You can get a value from the CompletableFuture using get() method. But keep in mind that get() is a blocking operation. None of the statements after get() will be executed unless and until it finishes it.
Let us take another example where I have more than one CompletableFuture and each of them is performing some task and in the end, I want all the CompletableFutures so as the perform post actions. Let us understand with an example.
The CompletableFuture provides a static method named allOf() which allows you to wait for the completion of all the Futures provided as a var arg. The return type of this method is CompletableFuture<Void>. The limitation of this method is that it does not return the combined results of all Futures. Instead, you have to manually get results from Futures. Fortunately, CompletableFuture.join() method and Java 8 Streams API make it simple. Here is the example for the same.
Unlike get() method which throws checked exception, the join() method throws an unchecked exception in case the Future does not complete normally. This makes it possible to use it as a method reference in the Stream.map().
To write callbacks, the CompletableFuture class provides various types of methods. Do try it out on your own.
That's it from this blog. I hope you guys understood how CompletableFuture works.