Designing a Flexible Java ThreadPoolExecutor: From Basics to Advanced Features
This article walks through building a custom thread pool executor in Java, starting with a simple thread creation example, evolving through multiple design iterations, and culminating in a full-featured implementation that supports core and maximum pool sizes, task queues, thread factories, and rejection policies.
Xiao Yu asks Flash to explain thread pools, feeling confused by the many parameters.
Flash starts with the simplest asynchronous execution:
<code>new Thread(r).start();</code>He then defines a basic
Executorinterface:
<code>public interface Executor {
void execute(Runnable r);
}</code>Flash implements a first version of a tool class:
<code>class FlashExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}
}</code>Realizing the need to control thread creation, they design a worker thread that pulls tasks from a queue, ensuring only a limited number of threads run:
<code>class FlashExecutor implements Executor {
// corePoolSize, workQueue, etc.
public void execute(Runnable r) {
tasks.offer(r);
}
// Worker threads continuously take tasks from the queue
}</code>Flash highlights three major benefits of this design: controlling thread count, decoupling task submission from execution via a queue, and reducing overhead from repeatedly creating and destroying threads.
Further improvements include:
Creating workers on demand instead of all at once, tracking created workers with
workCount.
Adding a
RejectedExecutionHandlerto let callers decide how to handle rejected tasks.
Introducing a
ThreadFactoryso new threads are created through a user‑provided factory.
They illustrate the enhanced design with diagrams (images omitted for brevity).
The final version mirrors the real
ThreadPoolExecutorconstructor:
<code>public FlashExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
// parameter validation omitted
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}</code>The parameters mean:
corePoolSize : number of core threads that stay alive.
maximumPoolSize : maximum number of threads allowed.
keepAliveTime : idle time before non‑core threads are terminated.
unit : time unit for
keepAliveTime.
workQueue : thread‑safe blocking queue holding pending tasks.
threadFactory : factory for creating new threads.
handler : policy for handling rejected tasks.
This comprehensive design ensures the thread pool can handle normal loads with core threads, scale up during spikes with additional threads up to
maximumPoolSize, and shrink back by terminating idle non‑core threads, while giving callers control over task rejection and thread creation.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.