Beauty of CompletableFuture and where should you use it
CompetableFuture is a new family member in java.util.concurrent package since Java 8. Many people like me are confused on where to use these fancy look classes although there’re a bunch of tutorials (simply google search it) explaining the basic functions of such classes. Here I’d like to introduce a scenario where CompletableFuture is really powerful and elegant.
Background
Say you have a main process and a threadpoolexecutor, you generate a lot of tasks and you want them to run in parallel if possible to gain performance.

A barebone implemention in Java would be like the following v1:
What if for each task, you want a return value ? This is where you would use Callable instead of Runnable. In addition, CompletionService is a wrapper of the ThreadPoolExecutor where it would return a list of Future handles and if you iterate through the service, it would always return the earliest finished tasks back, which saves a lot of time if you don’t want to get stuck with the most time consuming task in the middle. However, the drawback is that you can’t map the task and the returned Future object from CompletionService. This is ok if only one process sending the tasks to the threadpool but it’s not good if you have multiple processes and you want to do further operations after the task executions are done.
The sample code with CompletionService is like this:
So far so good. The background is too long and ThreadPoolExecutor/CompletionService would do good jobs . Now let’s get to the real meat:
Scenario

In multithreading condition, you have 10000 threads which contains a list of 20000 tasks and for each process after the tasks are finished running, you want to do further computation. well, all programs can be done in a serial way, a for loop will perfectly solves it. but if you want performance gain, you can’s miss CompletableFuture. Sample code is as following:
In the above, we declare a class Process and each of it has a list of takss to run. in the Main method, we have a list of 10000 processes and each has 20000 tasks. a reference of the executorPool is sent to each process so that the process can submit tasks to it. Notice that inside a process, after the tasks are submitted, a Future is put in the array taskArr. when taskArr.join() is called, it will block until all the Futures in the array has returned value.
The beauty of using CompletableFuture is that now you don’t need to create a costly ThreadPoolExecutor for each Process and all the tasks share the same ThreadPool. However, for each process, after task submission, it will then get an array of Future Handlers. once tasks are done, each process can continue and do their further computation nonblockingly.