navigation

Basics Of Using Java Future And Executor Service

Basics Of Using Java Future And Executor Service

by
October 3, 2017
Dreamix, frontpage, Java
No Comment

In this blog I will show you the basics of using Java Future and Executor Service. Combining the following:

and

can be very useful mechanism for solving tasks that have to be computed for a short time and processing the result of calculation further in the logic. In this article I am going to give you an example and some crib notes about using them in concurrent programming.

General Information

Future <V> is an interface that represents the result of an asynchronous computation. Once the computation is finished you can obtain the result of it by using get() method. Bear in mind that this is a blocking operation and waits until the outcome (V) is available.

future javaCalling get() is possible to take considerable amount of time. Instead of wasting time for waiting too long you can apply two approaches. The first one is working with get() as well but setting a timeout value as parameter which will prevent an endless stuck if something goes awry. The second way is by using isDone() method which takes a quick look on the Future and checks if it has finished its work or not.

ExecutorService represents abstraction of threads pool and can be created by the utility methods of Executors class. These methods can initialize a number of handful executors depending on the purpose they will be used for. There are some ways to delegate task to ExecutorService:

execute(Runnable) – returns void and cannot access the result

submit(Runnable or Callable<T>) – returns a Future object. The main difference is that when submitting Callable<T> the result can be accessed via the returned Future object

invokeAny(Collection<Callable<T>>) returns the result of one of the Callable objects that finished its work successfully. The rest tasks are cancelled.

invokeAll(Collection<Callable<T>>)) – returns a list of Future objects. All tasks are executed and the outcome can be obtained via the returned result list.

Last, when all tasks have finished their work, the threads in ExecutorService are still running. They are not destroyed yet and are in a “stand by” mode. This will make the JVM keep running. For the purpose of bypassing this problem Java offers you two methods – shutdown() and shutdownNow(). The key difference between them is stopping of ExecutorService. shutdown() will not stop it immediately and will wait all running threads to finish. Meanwhile ExecutorService will not accept new tasks. On the other hand shutdownNow() will try to stop it immediately. It will try to stop instantly all running tasks and to skip the processing of the waiting ones. The method returns a list of all running tasks for which there are no guarantees when will be stopped.

Java Future

Code Example

In order to illustrate the written above I have prepared a simple code demo. To start with, we have a class called CalculationTask implementing

Callable is an interface which stands for a task that returns a result after some computations. This class contains our business logic and every time a task starts, call() method is executed. In our case it contains calculations which take a lot of time to be completed.

CalculationTask

The next class which implementation I have not shown below, because it is irrelevant for the example is Result. It holds the result type of computation returned by call().

InvoiceCalculatorClient

This is the palace where we create an Executor service with fixed amount of threads. A separate task is created for every element in a list and is submitted it to the executor. Then we get the result of computation with the help of the returned Future object in order to operate with it as we need it.

Java Future

 

If you observe the example carefully, you will see a small issue in the for-loop that can cause a big problem in future. (Hint: get() is a blocking operation). It is obvious that get() method is called right away submitting a task to the executor. This means that the next task will not start its work before previous task finishes and there is no effect of multithreading approach in this case.

In the example above the first thing that we do is to start the tasks and to save the Future result. When we reach future.get() it is very likely to have nearly ready result. This time depends on the threads count and the logic of the task. Whether with complex logic or reasonable threads count the fix shows acceptable speed-up.
Another idea for solution is creating a collection of Callables<V> objects and using invokeAll(Collection<Callable>)).

Final words

The combination of Future and ExecutorService is a powerful instrument for background task execution, because its flexibility makes it suitable for:

-Time consuming calculations

-Calling web services or accessing remote services/resources

-Working with large-sized data structures

Feel free to share your experience or to ask questions on the topic in the comments section below.

Kostadin Hamanov

Java & Oracle Developer at Dreamix I would like to describe myself as a highly motivated young person with a strong desire and motivation to make a career in Software Engineering, Software Development and Information Technologies. I am ready to learn new things all the time in order to pursue my dream for professional success and personal development.

More Posts - Website

Follow Me:
LinkedIn

Do you want more great blogs like this?

Subscribe for Dreamix Blog now!