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)