Tech Master Tutorials
Email Facebook Google LinkedIn Pinterest Twitter
Home Java Java 8 Java Interview Questions Java8 Interview Questions Object Oriented Programming in Java JVM Java Programming

Java Concurrency Framework


In Java, threads can be created either extending the Thread class or implementing Runnable interface ( Runnable instance can be passed to a Thread object and the defined task in Runnable can be executed as part of the Thread).
Creating Threads in Java

Creating threads using Thread/Runnable and doing the thread management ourselves can be really complicated if we are working with large applications with a number of threads.

In case of large scale applications, having a separate thread management component abstracts the complexity and makes it easy to manage the threads. Java provides its own thread management constructs like executors, executor services, thread pools callable and future classes.

In Java concurrency model, Executers are used to create and manage the threads. We can pass Runnable or Callable tasks to the executors that can be executed as part of the Threads.
Creating threads using Runnable is explained in the section Creating Threads in Java.

Runnable instances implement the run() method and the task to be executed as part of the thread is defined in the run() method. Return type of the Runnable’s run method is void and does not return any value after the computation.

Callable can be used if we need to return a value after the thread execution. Callable is also like a Runnable which is a task to be executed as part of of a Thread. But Callable provides the capability to return a value as part of the computation.


We can create threads Pools that are executer implementations. And we can pass runnable/callable tasks to the executors to create and manage the threads for these tasks.

Java Concurrency Framework consists of the below core componenets:
Runnable/Calable
Executors – create and return Executor Service wrapped around a Thread pool.
ExecutorService – Executor service is the implemenatation of Executor, that create and executes the threads.
Thread Pools – can define multiple types of thread pools in java.
Future - Future object collect and return the value from the thread after execution completes.


Below is the example of running a Thread by passing a Runnable to the Executor. In the below example, Runnable is passed to an ExecutorService which in turn creates and manages the Thread execution part. Here we have created ExecutorService using Executors class with a SingleThread Execution capability. We can also create executor service with a thread pool.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecuterRunnable implements Runnable{

	@Override
	public void run() {
		System.out.println("Thread running ....");
		for (int i = 0; i < 5; i++) {
			System.out.println(i);
		}
	}
	
	public static void main(String[] args) {
		ExecuterRunnable runnableObj=new ExecuterRunnable();
		ExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        executor.execute(runnableObj);
	}

}

We can also create a thread pool as part of the executor and then pass the task to the executor that will be handled by one of the threads in thread pool. We will see some examples with thread pools in one of the below sections.

Running a thread using executor by passing a Callable instance:
In the below example, Callable task has been created by implementing the Callable Interface.

Callable also expects a type to be defined, the type of the response that is expected to be returned from the thread. Callable provides a call method that is like the run() method of the Runnable. But call method has a return type and using call method we get the response that is returned by the thread once the task is completed.
In the below example, we have a simple callable task that is returning an Integer value. So have provided the return type as Integer. Simply adding the numbers from 1 to 10 in for loop and returning the sum.

import java.util.concurrent.Callable;

public class ExecutorCallable implements Callable<Integer>{

	@Override
	public Integer call() throws Exception {
		int sum = 0;
		for (int i = 1; i < 10; i++) {
			sum+=i;
		}
		return sum;
	}
	
}


In the below example, we have the main class in which creating an instance of the Callable task and passing it to the executor using submit() method.
Following statement is used to create an executor service instance.
ExecutorService executor = Executors.newSingleThreadScheduledExecutor();

The newSingleThreadExecutor method creates an executor that executes a single task at a time. Here ExecutorService is being created, that can be used to execute a signle thread. We can also create an ExecutorService that can create and operate upon a thread pool.

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

public class MainClass_Callable {

	public static void main(String[] args) {
		ExecutorCallable callableObj = new ExecutorCallable();
		ExecutorService executor = Executors.newSingleThreadScheduledExecutor();
		Future<Integer> intResult = executor.submit(callableObj);

		try {
			if (!intResult.isDone()) {
				Thread.sleep(1000);
			}
			int result = intResult.get();
			System.out.println("result from future = " + result);
			executor.shutdown();
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}
	}
}

As part of the above example we also have Future class which holds the result returned from the thread.

Future:
A Future represents the result of an asynchronous computation.
Future provides below useful methods –
• get() – get is used to get the result of the asynchronous computation. If computation is still in progress thn get() method will block the program in which the get() is called. So get should be used with proper thought otherwise execution may block.
• isDone() – isDone() does not block. Simple returns true if thread is executed and result is available. In program isDone() can be used to check if asynchronous computation is done and then get()call can b made.
• Cancel() – cancellation of the task is done using the cancel method.

Java 8 onwards, CompletionStage interface and CompletableFuture implementation class has been introduced.
CompletionStage allows you to combine multiple threads serially. While excuting in serial, the result of one completion stage can be passed to another completion stage.
CompletionStage also allows to execute multiple threads concurrently and then combine.
CompletionStage/CompletableFuture provides wide variety of methods to manage threads.

Thread Pools:
Thread pools are used mostly in all the executor implementations. These thread pools consists of worker threads. Worker threads are not the Runnable or Callable tasks but are threads that are used to execute Runnable or Callable tasks.

In the below example, Callable instance has been created and passed to ExecutorService which operates upon a fixed thread pool.
5 has been passed as the argument to newFixedThreadPool() to notify the number of threads that will be managed by the fixed thread pool.
ThreadPool will have 5 threads to be used by the executor service. If any of the thread goes down then it will create a thread to make the count 5. There will always be 5 threads as part of the thread pool.
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class MainClass {

	public static void main(String[] args) {
		CallableTask callableObj = new CallableTask();
		ExecutorService executor = Executors.newFixedThreadPool(5);
		Future<Integer> intResult = executor.submit(callableObj);

		try {
			if (!intResult.isDone()) {
				Thread.sleep(1000);
			}
			int result = intResult.get();
			System.out.println("result from future = " + result);
			executor.shutdown();
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}
	}
}


import java.util.concurrent.Callable;

public class CallableTask implements Callable<Integer>{

	@Override
	public Integer call() throws Exception {
		int sum = 0;
		for (int i = 0; i < 10; i++) {
			sum+=i;
		}
		return sum;
	}
	
}


As we know that the Callable is a Functional Interface.
So instead of defining a Callable, then instantiating and then passing to the executor through submit,
we can pass a lambda expression for the same.


In the below example, passing lambda expression to the executor instead of callable instance.

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

public class MainClass {

	public static void main(String[] args) {
		ExecutorService executor = Executors.newFixedThreadPool(5);
		Future<Integer> intResult = executor.submit(
					() -> {
						int sum = 0;
						for (int i = 0; i < 10; i++) {
							sum +=i;
						}
						return sum;
					}
				);

		try {
			if (!intResult.isDone()) {
				Thread.sleep(1000);
			}
			int result = intResult.get();
			System.out.println("result from future = " + result);
			executor.shutdown();
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}
	}
	
}


Executors also provides newCachedThreadPool method to create an executor with an expandable thread pool.
In the below example, we are creating an executor which operates upon an expandable thread pool.

package concurrency.threadpools;

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

public class MainClass {

	public static void main(String[] args) {
		ExecutorService executor = Executors.newCachedThreadPool();
		Future<Integer> intResult = executor.submit(
					() -> {
						int sum = 0;
						for (int i = 0; i < 10; i++) {
							sum +=i;
						}
						return sum;
					}
				);

		try {
			if (!intResult.isDone()) {
				Thread.sleep(1000);
			}
			int result = intResult.get();
			System.out.println("result from future = " + result);
			executor.shutdown();
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}
	}
	
}