Executors Utility Class in Java

In this article, we will discuss important Concurrency Utility Executors class with examples. We will use Java 8 Lambda expressions in examples.
Executors Class provides factory and utility methods for Executor, ExecutorService, ScheduledExecutorService, ThreadFactory, and Callable classes defined in this package.
This class supports the following kinds of methods:
  • Methods that create and return an ExecutorService set up with commonly useful configuration settings.
  • Methods that create and return a ScheduledExecutorService set up with commonly useful configuration settings.
  • Methods that create and return a "wrapped" ExecutorService, that disables reconfiguration by making implementation-specific methods inaccessible.
  • Methods that create and return a ThreadFactory that sets newly created threads to a known state.
  • Methods that create and return a Callable out of other closure-like forms, so they can be used in execution methods requiring Callable.
Executors Utility class provides a list factory and utility methods are shown in below class diagram.
Executors Utility Class in Java
In this article, we will discuss five important utility methods of Executors class with example.
  1. Executors.newFixedThreadPool(int nThreads) Method
  2. Executors.newSingleThreadExecutor() Method
  3. Executors.newCachedThreadPool() Method
  4. Executors.newScheduledThreadPool Method
  5. Executors.newSingleThreadScheduledExecutor() Method

1. Executors.newFixedThreadPool(int nThreads) Method

Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue. At any point, at most nThreads threads will be active processing tasks. If additional tasks are submitted when all threads are active, they will wait in the queue until a thread is available. If any thread terminates due to a failure during execution prior to the shutdown, a new one will take its place if needed to execute subsequent tasks. The threads in the pool will exist until it is explicitly shutdown.
Parameters: nThreads the number of threads in the pool
Returns: the newly created thread pool
Throws: IllegalArgumentException - if nThreads <= 0
Syntax:
ExecutorService executorService = Executors.newFixedThreadPool(noOfThreads);

Java newFixedThreadPool example

Let’s create a very simple example.
Step 1: Create a Runnable task named “Task.java”.
class Task implements Runnable {
    @Override
    public void run() { 
         for (int i = 0; i < 5; i++) {
              System.out.println("[" + Thread.currentThread().getName() + "] " + "Message " + i);
              try {
                   Thread.sleep(200);
              } catch (final InterruptedException e) {
                   e.printStackTrace();
              }
         }
    }
}
Step 2: Let's create newFixedThreadPool() method and execute 5 tasks with two threads.
public class FixedThreadPoolExample {
 
     public static void main(final String[] args) throws InterruptedException, ExecutionException {
  
          System.out.println("Thread main started");
  
          final ExecutorService executorService = Executors.newFixedThreadPool(2);
          executorService.execute(new Task());
          executorService.execute(new Task());
          executorService.execute(new Task());
          executorService.execute(new Task());
          executorService.execute(new Task());
  
          executorService.shutdown();
  
          System.out.println("Thread main finished");
     }
}
Output:
Thread main started
[pool-1-thread-1] Message 0
[pool-1-thread-2] Message 0
Thread main finished
[pool-1-thread-1] Message 1
[pool-1-thread-2] Message 1
[pool-1-thread-1] Message 2
[pool-1-thread-2] Message 2
[pool-1-thread-1] Message 3
[pool-1-thread-2] Message 3
[pool-1-thread-2] Message 4
[pool-1-thread-1] Message 4
[pool-1-thread-1] Message 0
[pool-1-thread-2] Message 0
[pool-1-thread-1] Message 1
[pool-1-thread-2] Message 1
[pool-1-thread-1] Message 2
[pool-1-thread-2] Message 2
[pool-1-thread-1] Message 3
[pool-1-thread-2] Message 3
[pool-1-thread-1] Message 4
[pool-1-thread-2] Message 4
[pool-1-thread-1] Message 0
[pool-1-thread-1] Message 1
[pool-1-thread-1] Message 2
[pool-1-thread-1] Message 3
[pool-1-thread-1] Message 4
We have used new newFixedThreadPool, so when we have submitted 5 task, 2 new threads will be created and will execute 2 tasks. Other 3 tasks will wait in a wait queue. As soon as any task will be completed by a thread, another task will be picked by this thread and will execute it.

2. Executors.newSingleThreadExecutor() Method

This method creates an Executor that uses a single worker thread operating off an unbounded queue. (Note however that if this single thread terminates due to a failure during execution prior to the shutdown, a new one will take its place if needed to execute subsequent tasks.) Tasks are guaranteed to execute sequentially, and no more than one task will be active at any given time. Unlike the otherwise equivalent newFixedThreadPool(1) the returned executor is guaranteed not to be reconfigurable to use additional threads.
Note that Executors.newSingleThreadExecutor() Method returns the newly created single-threaded Executor.
Syntax:
ExecutorService executorService = Executors.newSingleThreadExecutor();

Executors.newSingleThreadExecutor() Method Example

In this example, let's instantiates a thread pool with a single thread. All tasks are executed sequentially by the same thread.
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/*
 * Instantiates a thread pool with a single thread
 * All tasks are executed sequentially by the same thread
 */

public class SingleThreadPoolExample {

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        System.out.println("Thread main started");

        Runnable task1 = () -> {
             System.out.println("Executing Task1 inside : " + Thread.currentThread().getName());
             try {
                 TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException ex) {
                 throw new IllegalStateException(ex);
            }
        };

        Runnable task2 = () -> {
             System.out.println("Executing Task2 inside : " + Thread.currentThread().getName());
             try {
                  TimeUnit.SECONDS.sleep(4);
             } catch (InterruptedException ex) {
                  throw new IllegalStateException(ex);
             }
        };

        Runnable task3 = () -> {
             System.out.println("Executing Task3 inside : " + Thread.currentThread().getName());
            try {
                 TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException ex) {
                 throw new IllegalStateException(ex);
            }
       };

       final ExecutorService executorService = Executors.newSingleThreadExecutor();
       System.out.println("Submitting the tasks for execution...");
       executorService.submit(task1);
       executorService.submit(task2);
       executorService.submit(task3);

       executorService.shutdown();

       System.out.println("Thread main finished");
    }
}
Output:
Thread main started
Submitting the tasks for execution...
Executing Task1 inside : pool-1-thread-1
Thread main finished
Executing Task2 inside : pool-1-thread-1
Executing Task3 inside : pool-1-thread-1
We have used newSingleThreadExecutor, so when we have submitted 3 tasks, 1 new thread will be created and will execute 1 task at a time. Other 2 tasks will wait in a wait queue. As soon as one task will be completed by a thread, another task will be picked by this thread and will execute it.

3. Executors.newCachedThreadPool() Method

This method creates a thread pool that creates new threads as needed but will reuse previously constructed threads when they are available. These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks.
Calls to execute will reuse previously constructed threads if available. If no existing thread is available, a new thread will be created and added to the pool. Threads that have not been used for sixty seconds are terminated and removed from the cache. Thus, a pool that remains idle for long enough will not consume any resources. Note that pools with similar properties but different details (for example, timeout parameters) may be created using ThreadPoolExecutor constructors.
Syntax:
final ExecutorService executorService = Executors.newCachedThreadPool();

Executors.newCachedThreadPool() Method Example

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

public class CachedThreadPoolExample {

    public static void main(final String[] args) throws InterruptedException, ExecutionException {

        System.out.println("Thread main started");

        Runnable task1 = () -> {
             System.out.println("Executing Task1 inside : " + Thread.currentThread().getName());
             try {
                  TimeUnit.SECONDS.sleep(2);
             } catch (InterruptedException ex) {
                  throw new IllegalStateException(ex);
             }
        };

        Runnable task2 = () -> {
             System.out.println("Executing Task2 inside : " + Thread.currentThread().getName());
             try {
                  TimeUnit.SECONDS.sleep(4);
             } catch (InterruptedException ex) {
                  throw new IllegalStateException(ex);
             }
       };

        Runnable task3 = () -> {
            System.out.println("Executing Task3 inside : " + Thread.currentThread().getName());
            try {
                 TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException ex) {
                 throw new IllegalStateException(ex);
            }
        };

        final ExecutorService executorService = Executors.newCachedThreadPool();
        System.out.println("Submitting the tasks for execution...");
        executorService.submit(task1);
        executorService.submit(task2);
        executorService.submit(task3);

        executorService.shutdown();

        System.out.println("Thread main finished");
    }
}
Output:
Thread main started
Submitting the tasks for execution...
Executing Task1 inside : pool-1-thread-1
Executing Task3 inside : pool-1-thread-3
Executing Task2 inside : pool-1-thread-2
Thread main finished

4. Executors.newScheduledThreadPool Method

This method creates a thread pool that can schedule commands to run after a given delay or to execute periodically.
newScheduledThreadPool Method returns ScheduledExecutorService interface
ScheduledExecutorService interface provides The schedule() method to create tasks with various delays and return a task object that can be used to cancel or check execution. The scheduleAtFixedRate() and scheduleWithFixedDelay() methods create and execute tasks that run periodically until cancelled.
Commands submitted using the Executor.execute(Runnable) and ExecutorService submit methods are scheduled with a requested delay of zero. Zero and negative delays (but not periods) are also allowed in schedule methods and are treated as requests for immediate execution.
Let's understand the methods of ScheduledExecutorService Interface and newScheduledThreadPool() factory method with an example.
public class SchedulingTasksWithScheduledThreadPool {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("Thread main started");

        // Create a task
        Runnable task1 = () -> {
            System.out.println("Executing the task1 at: " + new Date());
        };

         // Create a task
        Runnable task2 = () -> {
            System.out.println("Executing the task2 at: " + new Date());
        };
 
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);

        System.out.println("Scheduling task to run after 5 seconds... " + new Date());
        scheduledExecutorService.schedule(task1, 5, TimeUnit.SECONDS);
        scheduledExecutorService.schedule(task2, 5, TimeUnit.SECONDS);

        scheduledExecutorService.shutdown();
        System.out.println("Thread main finished");
    }
}
Output:
Thread main started
Scheduling task to run after 5 seconds... Sat Sep 01 10:56:40 IST 2018
Thread main finished
Executing the task1 at: Sat Sep 01 10:56:45 IST 2018
Executing the task2 at: Sat Sep 01 10:56:45 IST 2018
scheduledExecutorService.schedule() function takes a Runnable, a delay value, and the unit of the delay. The above program executes the task after 5 seconds from the time of submission.
Now let’s see an example where we execute the task periodically -
public class SchedulingTasksWithScheduledThreadPool {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("Thread main started");

        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

        // Create a task
        Runnable task1 = () -> {
            System.out.println("Executing the task1 at: " + new Date());
        };

        scheduledExecutorService.scheduleAtFixedRate(task1, 0, 2, TimeUnit.SECONDS);

        System.out.println("Thread main finished");
    }
}
Output:
Thread main started
Thread main finished
Executing the task1 at: Sat Sep 01 11:03:16 IST 2018
Executing the task1 at: Sat Sep 01 11:03:18 IST 2018
Executing the task1 at: Sat Sep 01 11:03:20 IST 2018
Executing the task1 at: Sat Sep 01 11:03:22 IST 2018
Executing the task1 at: Sat Sep 01 11:03:24 IST 2018
......
Note that if the task encounters an exception, subsequent executions of the task are suppressed. Otherwise, the task will only terminate if you either shut down the executor or kill the program.

5. Executors.newSingleThreadScheduledExecutor() Method

This method creates a single-threaded executor that can schedule commands to run after a given delay or to execute periodically. (Note however that if this single thread terminates due to a failure during execution prior to the shutdown, a new one will take its place if needed to execute subsequent tasks.) Tasks are guaranteed to execute sequentially, and no more than one task will be active at any given time. Unlike the otherwise equivalent newScheduledThreadPool(1) the returned executor is guaranteed not to be reconfigurable to use additional threads.
Syntax:
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

Executors.newSingleThreadScheduledExecutor() Method Example

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ExecutorsDemo {

    public static void main(String[] args) {
        ExecutorsDemo demo = new ExecutorsDemo();
        demo.newSingleThreadScheduledExecutor();
    }

    private void newSingleThreadScheduledExecutor() {
        System.out.println("Thread main started");

        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

        // Create a task
        Runnable task1 = () -> {
            System.out.println("Executing the task1 at: " + new Date());
        };

        scheduledExecutorService.scheduleAtFixedRate(task1, 0, 2, TimeUnit.SECONDS);

        System.out.println("Thread main finished");
    }
}
Output:
Thread main started
Thread main finished
Executing the task1 at: Mon Sep 10 13:15:57 IST 2018
Executing the task1 at: Mon Sep 10 13:15:59 IST 2018
Executing the task1 at: Mon Sep 10 13:16:01 IST 2018
Executing the task1 at: Mon Sep 10 13:16:03 IST 2018
Executing the task1 at: Mon Sep 10 13:16:05 IST 2018
Executing the task1 at: Mon Sep 10 13:16:07 IST 2018
...............
Learn multi-threading on Java Multithreading Tutorial
Learn advance concurrency on Java Concurrency Tutorial 

Comments