Kubernate

Monday, November 10, 2025

0013.Java ExecutorService

 Java ExecutorService

Introduction

ExecutorService in Java is a part of the java.util.concurrent package and provides a higher-level replacement for working with threads directly. It simplifies the process of managing a pool of threads and executing tasks asynchronously. ExecutorService offers various methods for submitting tasks, shutting down the executor, and managing the lifecycle of threads.

Key Points:

  • Thread Pool Management: Manages a pool of worker threads.
  • Task Submission: Provides methods to submit tasks for execution.
  • Lifecycle Management: Offers methods to gracefully shut down the executor service.
  • Concurrency Utilities: Part of the java.util.concurrent package, which provides robust concurrency utilities.

Table of Contents

1.     Creating an ExecutorService

2.     Submitting Tasks

3.     Shutting Down ExecutorService

4.     ScheduledExecutorService

5.     Example: Using ExecutorService

6.     Best Practices

7.     Real-World Analogy

8.     Conclusion

1. Creating an ExecutorService

Fixed Thread Pool

Creates a thread pool with a fixed number of threads.

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

public class FixedThreadPoolExample {

    public static void main(String[] args) {

        ExecutorService executor = Executors.newFixedThreadPool(3);

 

        for (int i = 0; i < 5; i++) {

            executor.execute(new Task());

        }

 

        executor.shutdown();

    }

}

 

class Task implements Runnable {

    @Override

    public void run() {

        System.out.println(Thread.currentThread().getName() + " is executing the task.");

    }

}

Cached Thread Pool

Creates a thread pool that creates new threads as needed but will reuse previously constructed threads when they are available.

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

public class CachedThreadPoolExample {

    public static void main(String[] args) {

        ExecutorService executor = Executors.newCachedThreadPool();

 

        for (int i = 0; i < 5; i++) {

            executor.execute(new Task());

        }

 

        executor.shutdown();

    }

}

 

class Task implements Runnable {

    @Override

    public void run() {

        System.out.println(Thread.currentThread().getName() + " is executing the task.");

    }

}

Single Thread Executor

Creates an executor that uses a single worker thread operating off an unbounded queue.

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

public class SingleThreadExecutorExample {

    public static void main(String[] args) {

        ExecutorService executor = Executors.newSingleThreadExecutor();

 

        for (int i = 0; i < 5; i++) {

            executor.execute(new Task());

        }

 

        executor.shutdown();

    }

}

 

class Task implements Runnable {

    @Override

    public void run() {

        System.out.println(Thread.currentThread().getName() + " is executing the task.");

    }

}

2. Submitting Tasks

Runnable Tasks

Tasks can be submitted to the executor using the execute() or submit() methods.

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

public class RunnableTaskExample {

    public static void main(String[] args) {

        ExecutorService executor = Executors.newFixedThreadPool(3);

 

        for (int i = 0; i < 5; i++) {

            executor.execute(new Task());

        }

 

        executor.shutdown();

    }

}

 

class Task implements Runnable {

    @Override

    public void run() {

        System.out.println(Thread.currentThread().getName() + " is executing the task.");

    }

}

Callable Tasks

Tasks that return a result can be submitted using the submit() method, which returns a Future.

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

import java.util.concurrent.ExecutionException;

 

public class CallableTaskExample {

    public static void main(String[] args) {

        ExecutorService executor = Executors.newFixedThreadPool(3);

 

        for (int i = 0; i < 5; i++) {

            Future<Integer> result = executor.submit(new Task());

            try {

                System.out.println("Result: " + result.get());

            } catch (InterruptedException | ExecutionException e) {

                e.printStackTrace();

            }

        }

 

        executor.shutdown();

    }

}

 

class Task implements Callable<Integer> {

    @Override

    public Integer call() throws Exception {

        int sum = 0;

        for (int i = 0; i < 5; i++) {

            sum += i;

        }

        return sum;

    }

}

3. Shutting Down ExecutorService

Graceful Shutdown

Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.

executor.shutdown();

Forceful Shutdown

Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.

executor.shutdownNow();

Await Termination

Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.

try {

    if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {

        executor.shutdownNow();

    }

} catch (InterruptedException e) {

    executor.shutdownNow();

    Thread.currentThread().interrupt();

}

4. ScheduledExecutorService

ScheduledExecutorService is an ExecutorService that can schedule commands to run after a given delay, or to execute periodically.

Example:

import java.util.concurrent.Executors;

import java.util.concurrent.ScheduledExecutorService;

import java.util.concurrent.TimeUnit;

 

public class ScheduledExecutorServiceExample {

    public static void main(String[] args) {

        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3);

 

        scheduler.scheduleAtFixedRate(new Task(), 0, 2, TimeUnit.SECONDS);

 

        try {

            Thread.sleep(10000);  // Run for 10 seconds

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

 

        scheduler.shutdown();

    }

}

 

class Task implements Runnable {

    @Override

    public void run() {

        System.out.println(Thread.currentThread().getName() + " is executing the task.");

    }

}

5. Example: Using ExecutorService

Comprehensive Example

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.TimeUnit;

 

public class ExecutorServiceExample {

    public static void main(String[] args) {

        ExecutorService executor = Executors.newFixedThreadPool(3);

 

        // Submitting Runnable tasks

        for (int i = 0; i < 5; i++) {

            executor.execute(new RunnableTask());

        }

 

        // Submitting Callable tasks

        for (int i = 0; i < 5; i++) {

            Future<Integer> result = executor.submit(new CallableTask());

            try {

                System.out.println("Result: " + result.get());

            } catch (InterruptedException | ExecutionException e) {

                e.printStackTrace();

            }

        }

 

        // Shutting down the executor

        executor.shutdown();

        try {

            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {

                executor.shutdownNow();

            }

        } catch (InterruptedException e) {

            executor.shutdownNow();

            Thread.currentThread().interrupt();

        }

    }

}

 

class RunnableTask implements Runnable {

    @Override

    public void run() {

        System.out.println(Thread.currentThread().getName() + " is executing the Runnable task.");

    }

}

 

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;

    }

}

Output:

pool-1-thread-1 is executing the Runnable task.

pool-1-thread-2 is executing the Runnable task.

pool-1-thread-3 is executing the Runnable task.

pool-1-thread-1 is executing the Runnable task.

pool-1-thread-2 is executing the Runnable task.

Result: 10

Result: 10

Result: 10

Result: 10

Result: 10

6. Best Practices

1.     Use Thread Pools: Use ExecutorService to manage thread pools efficiently.

2.     Properly Shutdown Executors: Always shut down executors to release resources.

3.     Handle Exceptions: Properly handle exceptions in tasks and during shutdown.

4.     Avoid Task Submission After Shutdown: Avoid submitting new tasks after calling shutdown().

5.     Use ScheduledExecutorService for Scheduling: Use ScheduledExecutorService for periodic or delayed task execution.

7. Real-World Analogy

Consider a restaurant kitchen where:

  • Thread Pool: The kitchen staff (threads) are part of a team that prepares meals.
  • Task Submission: Orders (tasks) are submitted to the kitchen staff.
  • Lifecycle Management: The kitchen opens (starts) and closes (shuts down) at specified times.
  • Scheduled Tasks: Some meals are prepared at regular intervals or need to be checked periodically.

8. Conclusion

ExecutorService in Java provides a high-level API for managing a pool of threads and executing tasks asynchronously. It simplifies thread management and offers methods for submitting tasks, shutting down executors, and scheduling tasks.

 

No comments:

Post a Comment

Spring Boot - Bean LifeCycle

 Here is a clear, step-by-step lifecycle of a Spring Boot application , explained in a simple + interview-ready way. 🔄 Spring Boot Applica...

Kubernate