This article is an addition to the Java Concurrency series and it covers about how to implement the Java interfaces ExecutorService and Futures. Previously we have discussed about thread pools so if you want to refresh, I recommend you to go though this first.
Java has a method to create a thread pool for us from a service called as ExecutorService.
The ExecutorService is an interface. So, it has pre-defined methods which we can use. We only need to know the method contract. Executors.newFixedThreadPool( 10 ) returns an object that has already implemented this interface for us.
We can execute a Runnable task from the executorService by invoking the method execute().
By using lambdas, this can also be written in a simplified syntax as;
execute() function returns void. That means, it runs the task inside the Runnable and returns nothing. But if we need to return some value from the thread, we can use executorService.submit() method.
Here, the submit() method returns an object that implemented the generic interface called as Future. So, we can assign this method to a Future variable as follows.
Here, note that in this example, we are returning a String as the output. In executorService.submit() method, we have to pass a Callable task as the parameter. (Though the Lambda syntax is similar to the previous!) Callable is similar to Runnable but it allows returning something. Previously in executorService.execute() method, we have passed a Runnable task and Runnable returns nothing.
So, to get the results from the Future object, there is a method in the Future future.get().
This may raise the question, how to know if the thread has finished its task and ready to output the future? The future.get() method is blocking, that means, if you call this method in a latter part of your main thread, the main thread waits if the task is not finished yet. This may be undesirable, so we can check if the task is done by the method future.isDone(). It outputs the boolean value true if the task inside the thread is done and future is available. If it’s not, we can set a timeout and wait for specific seconds.
It is important to shutdown() the executorService once all the tasks are finished, because the threads in the thread pool are not destroyed unless we explicitly shutdown the service. If you want a list of future tasks, you can do it from two steps.
Future Tasks List
1. create a list of Callable tasks. Here, as mentioned earlier, Callable is just like Runnable except that it returns a result while Runnable returns null.
Here, getCount() is a static synchronized method that I have used to differentiate each task, which returns a number.
2. Use executorService.invokeAll() function by passing the tasks list as a parameter.
Note that here also, the main thread will be blocked until an item’s task is completed.