DEV Community

Dev Cookies
Dev Cookies

Posted on

πŸ”₯ Mastering ThreadPool and Executors in Java (with Runnable Code Examples)

Concurrency is key to building scalable Java applications. The Executor framework in Java allows you to manage threads efficiently using thread pools. This article covers everything you need to know about ThreadPool and Executors with working code snippets.


🧡 What is a ThreadPool?

A ThreadPool manages a pool of worker threads, allowing tasks to be reused instead of creating new threads every time. This improves performance and resource utilization.


❌ Why Avoid new Thread()?

Using new Thread() for each task can:

  • 🧠 Cause high memory usage
  • ⚑️ Add overhead due to frequent thread creation
  • 🚨 Lead to resource exhaustion

πŸ› οΈ Java Executors Overview

1. Executor

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class SimpleExecutorExample {
    public static void main(String[] args) {
        Executor executor = Executors.newSingleThreadExecutor();
        executor.execute(() -> System.out.println("Executed by: " + Thread.currentThread().getName()));
    }
}
Enter fullscreen mode Exit fullscreen mode

2. ExecutorService

import java.util.concurrent.*;

public class ExecutorServiceExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        executorService.submit(() -> {
            System.out.println("Task 1 by " + Thread.currentThread().getName());
        });

        executorService.submit(() -> {
            System.out.println("Task 2 by " + Thread.currentThread().getName());
        });

        executorService.shutdown();
    }
}
Enter fullscreen mode Exit fullscreen mode

3. ThreadPoolExecutor

import java.util.concurrent.*;

public class CustomThreadPoolExecutorExample {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, 4, 60L, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(2),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        for (int i = 0; i < 6; i++) {
            int taskNumber = i;
            executor.submit(() -> {
                System.out.println("Task " + taskNumber + " executed by " + Thread.currentThread().getName());
            });
        }

        executor.shutdown();
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ“Š Built-in Executor Types

FixedThreadPool

ExecutorService fixedPool = Executors.newFixedThreadPool(3);
Enter fullscreen mode Exit fullscreen mode
  • Fixed number of threads, reuses them

CachedThreadPool

ExecutorService cachedPool = Executors.newCachedThreadPool();
Enter fullscreen mode Exit fullscreen mode
  • Dynamically adjusts thread count

SingleThreadExecutor

ExecutorService singleThread = Executors.newSingleThreadExecutor();
Enter fullscreen mode Exit fullscreen mode
  • Executes tasks sequentially using a single thread

ScheduledThreadPool

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);

scheduler.schedule(() -> {
    System.out.println("Delayed task executed by " + Thread.currentThread().getName());
}, 3, TimeUnit.SECONDS);
Enter fullscreen mode Exit fullscreen mode

πŸ”„ Custom ThreadPoolExecutor

ExecutorService customExecutor = new ThreadPoolExecutor(
    2, 4, 60L, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(10),
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.CallerRunsPolicy()
);
Enter fullscreen mode Exit fullscreen mode

Rejection Policies:

  • AbortPolicy – throws exception
  • CallerRunsPolicy – runs task in caller thread
  • DiscardPolicy – silently discards
  • DiscardOldestPolicy – discards oldest task

🚦 Shutting Down Executors

executorService.shutdown();
try {
    if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
        executorService.shutdownNow();
    }
} catch (InterruptedException e) {
    executorService.shutdownNow();
}
Enter fullscreen mode Exit fullscreen mode

πŸ” Real-World Example: Parallel Task Execution

import java.util.concurrent.*;
import java.util.*;

public class FileProcessorExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);
        List<String> files = Arrays.asList("file1.txt", "file2.txt", "file3.txt", "file4.txt");

        for (String file : files) {
            executor.submit(() -> {
                System.out.println("Processing " + file + " in thread " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000); // simulate delay
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        executor.shutdown();
    }
}
Enter fullscreen mode Exit fullscreen mode

βœ… Best Practices

  • Always shut down the executor
  • Prefer bounded queues for resource control
  • Use ThreadPoolExecutor for advanced tuning
  • Monitor pool stats (active threads, queue size)

πŸ€” Interview Questions

  1. Difference between Executor and ExecutorService?
  2. What happens if the task queue is full?
  3. When to use which rejection policy?
  4. Fixed vs Cached ThreadPool?
  5. How to handle exceptions in executor tasks?

πŸš€ TL;DR Summary

Type Thread Count Use Case
FixedThreadPool Fixed CPU-bound tasks
CachedThreadPool Dynamic Short-lived tasks
SingleThreadExecutor 1 Order-sensitive tasks
ScheduledThreadPool Fixed Periodic task scheduling

πŸ“† Coming Next

  • ✨ CompletableFuture and async chaining
  • ⚑ Performance tuning with real metrics
  • πŸ“ˆ Spring Boot + Async Tasks using @Async

Stay tuned!

Top comments (0)