← Back to list

Stop Creating Threads Manually: Master ThreadPoolExecutor, ThreadFactory, and BlockingQueue in Java

Modern backend systems process thousands of tasks simultaneously — API requests, background jobs, data processing, file uploads, and more…

balakrishnamakineni in Level Up Coding · 2026-03-06 13:21 · 72 claps · 4.3 min read paywalled
#multithreading-in-java #java-concurrency #threadpoolexecutor #blockingqueue #thread-factory
Open on Medium ↗
Wiki topics: FT · Fine-tuning & Adaptation 🌐 · Web Development 📚 · Books & Reading

Stop Creating Threads Manually: Master ThreadPoolExecutor, ThreadFactory, and BlockingQueue in Java

Modern backend systems process thousands of tasks simultaneously — API requests, background jobs, data processing, file uploads, and more. Creating a new thread for every task might seem simple, but it quickly becomes inefficient and dangerous.

Java solves this problem using the Executor Framework, especially ThreadPoolExecutor, combined with ThreadFactory and BlockingQueue.

If you are not a Member, you can read for free here :

In this article, we will walk through:

  • The problem with naive thread creation
  • How thread pools solve it
  • The role of ThreadPoolExecutor
  • Why BlockingQueue is critical
  • How ThreadFactory helps customize threads
  • Real-world production scenarios

The Problem: Creating Threads Manually

Many developers initially write code like this:

for(int i=0;i<1000;i++){
    new Thread(() -> {
        processTask();
    }).start();
}

At first glance this looks fine.

But imagine this in production where:

  • 10,000 requests arrive simultaneously
  • Each request creates a thread
  • Each thread consumes memory and CPU

Problems

  1. High Memory Usage: Each thread requires stack memory.
  2. Context Switching Overhead: Too many threads slow down the CPU scheduler.
  3. Uncontrolled Resource Usage: The system can crash due to thread explosion.
  4. No Task Queueing: If threads are busy, new tasks cannot wait.

This is where Thread Pools come in.

The Solution: Thread Pools

Instead of creating new threads for every task:

  • Create a fixed number of reusable threads
  • Put tasks into a queue
  • Worker threads pick tasks from the queue

This is exactly what ThreadPoolExecutor does.

Java provides it via:

java.util.concurrent.ThreadPoolExecutor

What is ThreadPoolExecutor?

ThreadPoolExecutor is the core implementation of Java’s thread pool system.

It manages:

Basic Constructor:

ThreadPoolExecutor executor = new ThreadPoolExecutor(
        corePoolSize,
        maximumPoolSize,
        keepAliveTime,
        TimeUnit.SECONDS,
        workQueue,
        threadFactory,
        handler
);

Important Parameters

How ThreadPoolExecutor Works Internally

Understanding the execution flow helps design better systems.

1. Task Submission

executor.submit(task);

2. Check Core Threads

If active threads < corePoolSize

➡ create a new thread.

3. Add to Queue

If core threads are busy

➡ task goes into BlockingQueue

4. Create Extra Threads

If queue is full and threads < maximumPoolSize

➡ create new thread.

5. Reject Task

If queue is full and max threads reached

RejectedExecutionHandler is triggered.

What is BlockingQueue?

A BlockingQueue is a thread-safe queue where:

  • Producers add tasks
  • Worker threads take tasks

If queue is empty: ➡ worker thread waits

If queue is full: ➡ producer waits

This prevents:

  • CPU wastage
  • busy waiting
  • race conditions

Common BlockingQueue Implementations

Example:

BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(100);

This means only 100 tasks can wait.

What is ThreadFactory?

When using ThreadPoolExecutor, new worker threads need to be created whenever the pool expands. Instead of creating threads directly using new Thread(), Java delegates this responsibility to a ThreadFactory.

ThreadFactory is an interface located in the java.util.concurrent package.

public interface ThreadFactory {
    Thread newThread(Runnable r);
}

This interface contains only one method:

Thread newThread(Runnable r)

Its responsibility is simple:

Create and return a new Thread that will execute the given Runnable task.

So whenever a thread pool needs a new worker thread, it calls this method.

Why Does ThreadFactory Exist?

Without ThreadFactory, the executor would create threads like this internally:

new Thread(task)

This is fine for simple programs, but in production systems we often need more control over threads.

Examples of customization:

  • Set thread names
  • Set thread priority
  • Configure daemon threads
  • Add logging or monitoring
  • Assign thread groups

ThreadFactory allows us to control how threads are created.

Default Implementation of ThreadFactory Java already provides a default implementation internally called:

Executors.DefaultThreadFactory

When you create a thread pool like this:

ExecutorService executor = Executors.newFixedThreadPool(5);

Java internally uses DefaultThreadFactory. Threads created by it look like:

pool-1-thread-1
pool-1-thread-2
pool-1-thread-3

This naming convention helps debugging.

Internal Flow of Thread Creation

When ThreadPoolExecutor needs a new thread, the following happens:

Task Submitted
      │
      ▼
ThreadPoolExecutor checks thread count
      │
      ▼
Need new worker thread?
      │
      ▼
Call ThreadFactory.newThread(Runnable)
      │
      ▼
ThreadFactory creates Thread
      │
      ▼
Thread starts executing task

So the ThreadFactory acts as a thread creation strategy.

Simple Custom ThreadFactory Example: Now let’s create our own custom thread factory.

import java.util.concurrent.ThreadFactory;

public class CustomThreadFactory implements ThreadFactory {
    private int counter = 0;
    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r);
        thread.setName("worker-thread-" + counter++);
        thread.setPriority(Thread.NORM_PRIORITY);
        return thread;
    }
}

What this does:

  • Assigns custom thread names
  • Sets thread priority

Real-World Production Scenario

Let’s say you are building a payment processing system.

Requests arrive:

  • Validate payment
  • Fraud detection
  • Database update
  • Notification sending

Instead of creating threads for each request:

You create a ThreadPoolExecutor.

Architecture

Incoming Tasks
      │
      ▼
BlockingQueue
      │
      ▼
Worker Threads (ThreadPoolExecutor)
      │
      ▼
Process Tasks

Benefits:

  • Controlled concurrency
  • Efficient CPU usage
  • Predictable system behavior

Example Production Ready Code

import java.util.concurrent.*;

public class ExecutorExample {
    public static void main(String[] args) {
        ThreadPoolExecutor executor =
                new ThreadPoolExecutor(
                        2,
                        4,
                        30,
                        TimeUnit.SECONDS,
                        new ArrayBlockingQueue<>(10),
                        new CustomThreadFactory(),
                        new ThreadPoolExecutor.AbortPolicy()
                );
        for (int i = 0; i < 20; i++) {
            int taskId = i;
            executor.submit(() -> {
                System.out.println(Thread.currentThread().getName()
                        + " processing task " + taskId);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
        executor.shutdown();
    }
}

When Should You Use ThreadPoolExecutor?

Use it when:

✔ Processing API requests ✔ Handling background jobs ✔ Processing message queues ✔ Batch processing ✔ File uploads / downloads ✔ Real-time data pipelines

Large companies use it in:

  • microservices
  • backend APIs
  • message processing systems
  • high-throughput servers

Key Takeaways

  • Creating threads manually does not scale.
  • ThreadPoolExecutor manages worker threads efficiently.
  • BlockingQueue stores tasks safely between producers and workers.
  • ThreadFactory allows customization of thread creation.
  • Together they provide scalable concurrency control in Java systems.

Understanding these components is essential for building high-performance backend systems.

👉 If you found this article helpful, follow me here on Medium and also don’t forget to clap and comment your thoughts. Connect with me on LinkedIn


메타데이터
post_id
fca98478646f
slug
stop-creating-threads-manually-master-threadpoolexecutor-threadfactory-and-blockingqueue-in-java-fca98478646f
url
https://levelup.gitconnected.com/stop-creating-threads-manually-master-threadpoolexecutor-threadfactory-and-blockingqueue-in-java-fca98478646f
canonical_url
https://levelup.gitconnected.com/stop-creating-threads-manually-master-threadpoolexecutor-threadfactory-and-blockingqueue-in-java-fca98478646f
author_url
https://medium.com/@balakrishna-02
status
ok
fetched_at
2026-08-02 03:37:55