Learnitweb

Executor framework in Java – an introduction

The executor framework is Java enables you to separate thread creation and management for the implementation of concurrent tasks. With executor framework, you don’t have to worry about creation and management of threads, only create tasks and submit to the executor.

Let us see important classes and interfaces of executor framework.

1. Executor

This is an interface in java.util.concurrent package. An executor executes submitted Runnable tasks. This is the main interface in executor framework. This interface provides a way to separate task submission from the thread management. The separation of creation and management of tasks is done by submitting tasks to the executor rather that manually starting the thread by calling start(), like:

executor.execute(runnableTask1);
executor.execute(runnableTask2); 

Here, executor is an instance of Executor interface. We’ll see later in this tutorial ways to create an Executor.

This interface has one method submit with following signature:

void execute(Runnable task)

This method is used to execute the submitted tasks. Since this method has no return type, this method can not be used to track progress of the submitted task. Following are sub-interfaces of Executor:

  • ExecutorService
  • ScheduledExecutorService

2. ExecutorService

This interface is a sub-interface of Executor and is present in java.util.concurrent package. This interface has few methods which return a Future object, which is used for tracking progress of submitted tasks.

Following are its implementing classes:

  • AbstractExecutorService
  • ForkJoinPool
  • ScheduledThreadPoolExecutor
  • ThreadPoolExecutor

This interface has an overloaded method submit() which is used to submit the tasks to executor for execution. Unlike the execute() method in Executor interface, this method returns a Future object that can be used to cancel execution and/or wait for completion. Following are overloaded versions of submit():

  • <T>Future<T> submit(Callable<T> task)
  • Future<?> submit(Runnable task)
  • <T>Future<T> submit(Runnable task, T result)

3. Executors

This is a class present in java.util.concurrent package. This class provides factory and utility methods for Executor, ExecutorService, ScheduledExecutorService, ThreadFactory, and Callable. Few important methods provided in this class are:

  • static Callable<Object> callable(Runnable task)
  • static ExecutorService newFixedThreadPool(int nThreads)
  • static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
  • static ExecutorService newSingleThreadExecutor()
  • static ThreadFactory privilegedThreadFactory()

4. Callable

This is an interface present in java.util.concurrent package. Callable represents a task that returns a result and may throw an exception. This interface has single method call():

V call()

5. Future

A Future represents the result of an asynchronous task. Methods of Future allows you to check the progress of the task. With Future object you can check whether the task is complete or not and can get the result of task. Future provides methods to cancel the task or check if the task is cancelled or not, wait for the completion of the task and get the result and check if the task is done. Following are the methods provided by the Future interface:

  • boolean cancel(boolean mayInterruptIfRunning)
  • V get()
  • V get(long timeout, TimeUnit unit)
  • boolean isCancelled()
  • boolean isDone()

6. Example

Let us see usage of executor framework with an example. In this example, we’ll create ten Runnable tasks and submit the tasks to a fixed thread pool of size 2. We’ll verify that at only two threads are running at a time.

We suggest you to run this program and observe the output in console. You’ll observe that at a given point of time only 2 tasks are running. However, we have submitted 10 tasks to the executor.

package com.learnitweb;

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

class Task implements Runnable {
	private String name;

	public Task(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void run() {
		try {
			Long duration = (long) (Math.random() * 10);
			System.out.println("Executing: " + name);
			TimeUnit.SECONDS.sleep(2);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

public class ExecutorExample {
	public static void main(String args[]) {
		ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);

		for (int i = 1; i <= 10; i++) {
			Task task = new Task("Task " + i);
			System.out.println("Submitting: " + task.getName());
			executor.execute(task);
		}
		executor.shutdown();
	}
}

Output

Submitting: Task 1
Submitting: Task 2
Submitting: Task 3
Submitting: Task 4
Submitting: Task 5
Submitting: Task 6
Submitting: Task 7
Submitting: Task 8
Submitting: Task 9
Submitting: Task 10
Executing: Task 1
Executing: Task 2
Executing: Task 3
Executing: Task 4
Executing: Task 5
Executing: Task 6
Executing: Task 7
Executing: Task 8
Executing: Task 9
Executing: Task 10