How to use Java Executor framework for Multithreading

How to use Java Executor framework for Multithreading

How to use Java Executor framework for Multithreading

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 CallableInterface. 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;
    }

}

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();
            }
        }

    }

}

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

Callable<Integer> callable = new CallableTask();

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);

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]);

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();
}

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();
            }
        }

    }

}

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.

A Thread pool with 4 threads

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();
        }

    }

}

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);

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);

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();
}

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

Some possible Scenarios of the Fixed Thread Pool

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

    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();

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

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

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

Further reading:

Send Email in Java

Achieving Functional Programming in Java

Functional Programming? Don’t Even Bother, It’s a Silly Toy

The Differences Between a Junior, Mid-Level and Senior Developer

Object-Oriented Programming is Bad

Object-Oriented Programming — The Trillion Dollar Disaster

Java 11 & Spring Boot 2.2 Tutorial: Build your First REST API App

Building a Secure API with GraphQL & Spring Boot

Java Reflection API Tutorial

Best Java machine learning library

java

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

How to Install OpenJDK 11 on CentOS 8

What is OpenJDK? OpenJDk or Open Java Development Kit is a free, open-source framework of the Java Platform, Standard Edition (or Java SE).

Java Core (2020) | Java for beginners | Brush up your Java Skills | Get Hired

In this video we will learn about all the major concepts that come under Java Core . The course is very carefully designed and is made with keeping simplicit...

Java File Class Tutorial | Java.io File Class in Java Example

Java File class represents the path of directories and files. It provides the methods for renaming, deleting, and obtaining the properties of file or directory.

Best Ways To Improve Skills In Java and Python Programming

Join CETPA, India's best institute for Java online training and Certification Course. Enroll now to learn Core to Advanced Java & avail 50% discount on Java Online Course with Certificate.

Java Fundamentals: Learn Java for absolute beginners |Simpliv

Java Fundamentals: Learn Java for absolute beginners