ExecutorService in Java



ExecutorService Java Multithreading

ExecutorService in Java is an interface that is part of the java.util.concurrent package. This Java concurrency utility helps to execute asynchronous tasks concurrently. Using the ExecutorService interface, we can separate the task creation and task execution process. It is a subinterface of the ExecutorFramework. In this tutorial, we will discuss in detail Java ExecutorService, how to instantiate a service along with its methods and different examples.

ExecutorService in Java

ExecutorService Framework

In a multithreaded environment, we may have to work on multiple threads concurrently. Sometimes, this can be very difficult and hence may need a framework to handle the thread creation and execution separately. For this, Java 1.5 introduced the concept of the ExecutorService framework. This framework contains 3 main interfaces namely Executor, ExecutorService, and ThreadPoolExecutor.

The main purpose of an ExecutorService is to manage a number of threads and also assign tasks to the threads. In case, the number of tasks is more than the number of threads, it queues up the tasks until any thread is available for execution.

Methods of ExecutorService

Below are the main methods of the ExecutorService interface.

MethodDescription
boolean awaitTermination(long timeout, TimeUnit timeunit)Blocks until all tasks have completed execution after shutdown or after a particular timeout.
void execute(Runnable command)Executes the given command
List<Future> invokeAll(Collections task)Executes the given tasks and returns the list of futures holding their status and results when complete
List<Future> invokeAll(Collections task, long timeout, TimeUnit timeunit)Executes the given tasks and returns the list of futures holding their status and results when complete or when timeout occurs
T invokeAny(Collections tasks)Executes the given task and returns the result of the completed task
T invokeAny(Collections task, long timeout, TimeUnit timeunit)Executes the given task and returns the result of the completed task before the timeout occurs.
boolean isShutDown()Returns true if the executor is shutdown
boolean isTerminated()Returns true if the execution of all task is complete after shutdown
void shutDown()Initiates a shutdown for all the submitted tasks
List shutDownNow()Attempts to stop all actively executing tasks and returns a list of all awaiting task
Future submit(Callable task)Submits a value returning task for execution and returns the future of pending result task
Future submit(Runnable task)Submits a Runnable task for execution.
Future submit(Runnable task, T result)Submits a Runnable task and returns the Future representing the task.

Creating an ExecutorService instance

We can create an ExecutorService instance in the below 3 ways:

ExecutorService exec = Executors.newSingleThreadExecutor();
ExecutorService exec = Executors.newFIxedThreadPool(int count);
ExecutorService exec = Executors.newScheduledThreadPool(int count);

Assign task using Java ExecutorService execute() method

The below example shows how to execute an asynchronous task using the execute() method of the ExecutorService. In this example, we create a single thread instance using the newSingleThreadExecutor. The execute() method takes the Runnable object as a parameter. Finally, after the task execution, we can check if the ExecutorService is shutdown using the isShutDown() method.

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

public class ExecutorServiceDemo {

  public static void main(String[] args) {
    ExecutorService exec = Executors.newSingleThreadExecutor();
    exec.execute(new Runnable() {

      @Override
      public void run() {
        System.out.println("Example of execute method");
        
      }
      
    });
    exec.shutdown();
    System.out.println("Is ExecutorService Shutdown: " + exec.isShutdown());
  }

}
Example of execute method
Is ExecutorService Shutdown: true

Assign task using the Java ExecutorService submit() method

This is an example of the ExecutorService submit() method that accepts the Runnable task as a parameter.

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

public class ExecutorServiceDemo {

  public static void main(String[] args) {
    ExecutorService exec = Executors.newSingleThreadExecutor();
    exec.submit(new Runnable() {
      @Override
      public void run() {
        System.out.println("Example of submit method");
        
      }
    });
    
    exec.shutdown();
  }

}
Example of submit method

Assign task using the ExecutorService invokeAny() method

This example shows how to use the invokeAny() method of the Java ExecutorService. We need to pass the Callable object or action as a parameter to the invokeAny() method. Whichever callable action it executes, it returns the result of the corresponding task.

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.*;

public class ExecutorServiceDemo {

  public static void main(String[] args) throws InterruptedException, ExecutionException {
    ExecutorService exec = Executors.newSingleThreadExecutor();
    Set<Callable<String>> c = new HashSet<Callable<String>>();
    
    c.add(new Callable<String>() {

      public String call() throws Exception {
        return "Callable Task1";
      }
      
    });
    
    c.add(new Callable<String>() {

      public String call() throws Exception {
        return "Callable Task2";
      }
      
    });
    
    String value = exec.invokeAny(c);
    System.out.println(value);
    
    exec.shutdown();
  }

}
Callable Task1

Assign task using the ExecutorService invokeAll() method

This example shows how to use the Java ExecutorService interface invokeAll() method that executes all the Callable actions. It returns the result of the task execution in the form of Future object.

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.*;

public class ExecutorServiceDemo {

  public static void main(String[] args) throws InterruptedException, ExecutionException {
    ExecutorService exec = Executors.newSingleThreadExecutor();
    Set<Callable<String>> c = new HashSet<Callable<String>>();
    
    c.add(new Callable<String>() {
      @Override
      public String call() throws Exception {
        return "Callable Task1";
      }
      
    });
    
    c.add(new Callable<String>() {
      @Override
      public String call() throws Exception {
        return "Callable Task2";
      }
      
    });
    
    List<Future<String>> l = exec.invokeAll(c);
    for(Future<String> f: l)
      System.out.println(f.get());

    exec.shutdown();
  }

}
Callable Task1
Callable Task2

Important points on ExecutorService

  • Always remember to shutdown the ExecutorService once the task is completed. Never keep the unused ExecutorService alive.
  • Make proper use of the Threadpool capacity while using a fixed-length thread pool
  • We cannot call the get() method of the Future interface after canceling the task. This will throw a CancellationException.
  • Use relevant timeouts wherever required to avoid unnecessary blocking time.

Reference