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.
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.
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.
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.
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();
}
}
}
}
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.
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.
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.
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.
All the code discussed in this article can be found in this git repo
Now you know the following concepts
In my future Articles I will be covering more topics on Multithreading
Further reading:
☞ 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