DEV Community

Cover image for How to use Java Executor framework for Multithreading
Aditya Sridhar
Aditya Sridhar

Posted on • Originally published at adityasridhar.com

How to use Java Executor framework for Multithreading

This post was originally published in adityasridhar.com

In my Previous Blog I covered the basics of Multithreading in Java. Click here to read that blog.

The previous Blog covered how to create Threads by Extending the Thread class and implementing the Runnable Interface.

This article will be covering 2 topics.

  • Creating Threads by implementing the Callable Interface
  • Using the Executor Framework in Java

Implementing the Callable Interface

In order to create a Piece of code which can be run in a Thread, we create a class and then implement the Callable Interface. The task being done by this piece of code needs to be put in the call() function. In the below code you can see that CallableTask is a class which implements Callable Interface, and the task of summing up numbers from 0 to 4 is being done in the function.

import java.util.concurrent.Callable;
class CallableTask implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {

        int sum = 0;
        for (int i = 0; i < 5; i++) {
            sum += i;
        }
        return sum;
    }

}
Enter fullscreen mode Exit fullscreen mode

In the above code you would notice that Callable has a parameter of Integer. This shows that the return type of this Callable will be Integer. Also it can be seen that the call function returns Integer type.

Creating the Threads and running them

The code below shows how to create the Threads and then run them.

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableInterfaceDemo {

    public static void main(String[] args) {
        FutureTask<Integer>[] futureList = new FutureTask[5];

        for (int i = 0; i <= 4; i++) {
            Callable<Integer> callable = new CallableTask();
            futureList[i] = new FutureTask<Integer>(callable);
            Thread t = new Thread(futureList[i]);
            t.start();

        }

        for (int i = 0; i <= 4; i++) {
            FutureTask<Integer> result = futureList[i];
            try {
                System.out.println("Future Task" + i + ":" + result.get());
            } catch (InterruptedException e) {

                e.printStackTrace();
            } catch (ExecutionException e) {

                e.printStackTrace();
            }
        }

    }

}
Enter fullscreen mode Exit fullscreen mode

In order to create a Thread, first we need to create an Instance of CallableTask which implements the Callable Interface as shown in

Callable<Integer> callable = new CallableTask();
Enter fullscreen mode Exit fullscreen mode

Then we need to create an Instance of the FutureTask class and pass the instance of Callable task as an argument as shown in

futureList[i] = new FutureTask<Integer>(callable);
Enter fullscreen mode Exit fullscreen mode

Then to create a Thread we create an instance of the Thread class and pass the Instance of the FutureTask class as an argument as shown in

Thread t = new Thread(futureList[i]);
Enter fullscreen mode Exit fullscreen mode

Finally the Thread is started with the start() function.

Getting the result from the Thread

In case of Callables the Thread can actually return a value. In order to get this value we can call the get() function on the instance of the FutureTask. In our code, the return value of the thread is the sum of numbers from 0 to 4.

This is shown in the below code snippet

FutureTask<Integer> result = futureList[i];
try {
    System.out.println("Future Task" + i + ":" + result.get());
} catch (InterruptedException e) {

    e.printStackTrace();
} catch (ExecutionException e) {

    e.printStackTrace();
}
Enter fullscreen mode Exit fullscreen mode

Also the thread may throw an Exception as well which can be handled with try catch blocks.

Complete Code

Here is the complete code discussed till now

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class CallableTask implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {

        int sum = 0;
        for (int i = 0; i < 5; i++) {
            sum += i;
        }
        return sum;
    }

}

public class CallableInterfaceDemo {

    public static void main(String[] args) {
        FutureTask<Integer>[] futureList = new FutureTask[5];

        for (int i = 0; i <= 4; i++) {
            Callable<Integer> callable = new CallableTask();
            futureList[i] = new FutureTask<Integer>(callable);
            Thread t = new Thread(futureList[i]);
            t.start();

        }

        for (int i = 0; i <= 4; i++) {
            FutureTask<Integer> result = futureList[i];
            try {
                System.out.println("Future Task" + i + ":" + result.get());
            } catch (InterruptedException e) {

                e.printStackTrace();
            } catch (ExecutionException e) {

                e.printStackTrace();
            }
        }

    }

}
Enter fullscreen mode Exit fullscreen mode

The Executor Framework

Creating a Thread on the Fly everytime is Resource Intensive. One good alternative for this is to have some Threads already setup and then allocate our tasks to these threads. This is where the Executors Class and ExecutorService are very useful.

Thread pool

The above image shows a Thread pool with 4 threads. Whenever we want any task to be run, we can assign it to these threads. Once the task is complete, the Thread will be freed to take up other tasks.

How to use the Executor Framework

Here is a code which uses the Executor framework.

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

class Worker implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {

        int sum = 0;
        for (int i = 0; i < 5; i++) {
            sum += i;
        }
        return sum;
    }

}

public class ExecutorDemo {

    public static void main(String[] args) {
        ExecutorService executors = Executors.newFixedThreadPool(4);
        Future<Integer>[] futures = new Future[5];
        Callable<Integer> w = new Worker();
        try {
            for (int i = 0; i < 5; i++) {
                Future<Integer> future = executors.submit(w);
                futures[i] = future;

            }

            for (int i = 0; i < futures.length; i++) {
                try {
                    System.out.println("Result from Future " + i + ":" + futures[i].get());
                } catch (InterruptedException e) {

                    e.printStackTrace();
                } catch (ExecutionException e) {

                    e.printStackTrace();
                }
            }
        } finally {
            executors.shutdown();
        }

    }

}

Enter fullscreen mode Exit fullscreen mode

First we create a Worker Class which implements Callable and does the task which we need.

Next we need to create an ExecutorService.

The Executors class has multiple implementations of the ExecutorService.

Let us use the Executors class to create a fixed Thread pool of size 4. This is done as follows

ExecutorService executors = Executors.newFixedThreadPool(4);
Enter fullscreen mode Exit fullscreen mode

Next we need to submit our task to the Executor Service. This is done using the following Line of code

Future<Integer> future = executors.submit(w);
Enter fullscreen mode Exit fullscreen mode

On submitting the task we get an Instance of the Future Object. The Future Object is what will store the result of the Task.

Getting the result of the Thread

In order to get the result of each task, we can call the get() method of the Future Instance.
This is shown in the below code snippet.

try {
    System.out.println("Result from Future " + i + ":" + futures[i].get());
} catch (InterruptedException e) {

    e.printStackTrace();
} catch (ExecutionException e) {

    e.printStackTrace();
}
Enter fullscreen mode Exit fullscreen mode

The thread can also Throw an Exception which can be handled using try catch.

Some possible Scenarios of the Fixed Thread Pool

  • In this example we created a fixed Thread pool of size 4
  • If we submit a total of 3 tasks to this ExecutorService, then all 3 tasks will be assigned to the thread pool and they will start executing.
  • If we submit 4 tasks to this ExecutorService, then again all 4 tasks will be assigned to the thread pool and they will start executing.
  • Now if we submit 5 tasks to this thread pool. Only 4 of the tasks will be assigned to the thread pool. This is because the size of the Thread pool is 4. The 5th Task will be assigned only when one of the threads in the pool gets freed

Shutting down the ExecutorService

The ExecutorService needs to be shutdown when the threads are not needed anymore. This will ensure that the JVM is not consuming Additional Resources.

The ExecutorService can be shutdown using the following command

executors.shutdown();
Enter fullscreen mode Exit fullscreen mode

It can be seen that this shutdown is put within the finally block. This is to ensure that the shutdown is always executed at the end of the code even if any exception occured.

If the shutdown is not done in the right way, then in case any exception occurs then the ExecutorService will still be running and will be consuming Additional JVM resources.

Code

All the code discussed in this article can be found in this git repo

Congrats😃

Now you know the following concepts

  • Using the Callable Interface
  • Using the Java Executor Framework for Multithreading.

In my future Articles I will be covering more topics on Multithreading

Feel free to connect with me in LinkedIn or follow me in twitter.

If you liked this post, you can checkout my website https://adityasridhar.com for other similar posts

Top comments (0)