Fundamentals 30 min read

Deep Dive into Java ThreadPoolExecutor: Design, States, and Source Code Analysis

This article provides an in‑depth technical analysis of Java's ThreadPoolExecutor, explaining its state encoding, task submission flow, worker creation, lock mechanisms, task execution loop, shutdown procedures, and practical examples that illustrate how core size, maximum size, and queue types affect thread pool behavior.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Deep Dive into Java ThreadPoolExecutor: Design, States, and Source Code Analysis

ThreadPoolExecutor is a core Java class that manages a pool of reusable threads to avoid the high cost of creating and destroying threads, and the article begins with an overview of why thread pools are needed.

The executor’s state is encoded in a 32‑bit integer where the high 3 bits represent the run state (RUNNING, SHUTDOWN, STOP, TIDYING, TERMINATED) and the low 29 bits represent the worker count; this packing technique is common in Android source code.

The execute(Runnable command) method is the entry point for submitting tasks. It creates new core threads while the worker count is below corePoolSize, otherwise it attempts to queue the task; if queuing fails it tries to add a non‑core thread, and finally applies the rejection policy.

The addWorker(Runnable firstTask, boolean core) method contains a double‑nested loop that first checks the pool state, then verifies the worker count against the appropriate bound (core or maximum). It uses CAS to increment the worker count and, under a lock, creates a Worker object, adds it to the workers set, and starts the underlying thread. Worker is a private final inner class that extends AbstractQueuedSynchronizer, giving each worker a non‑reentrant lock used to control whether the thread may be interrupted while executing a user task.

The runWorker(Worker w) loop repeatedly obtains tasks via getTask(), locks the worker to prevent interruption during task execution, runs beforeExecute, the task itself, and afterExecute, handling all exceptions and finally calling processWorkerExit. getTask() blocks on the work queue, respects shutdown and stop states, and may time‑out for non‑core threads, reducing the worker count when appropriate. processWorkerExit removes the worker from the set, updates completed task counts, and may replace the worker if the pool is still running and the minimum number of threads is not satisfied.

The termination logic in tryTerminate() transitions the pool through TIDYING to TERMINATED when no workers remain and the queue is empty, interrupting idle workers if needed.

Shutdown methods ( shutdown() and shutdownNow()) change the run state, interrupt idle workers, and optionally drain the queue, illustrating the difference between graceful and abrupt termination.

Two practical examples demonstrate how core size, maximum size, and queue type affect thread creation, task rejection, and shutdown behavior, including a case with corePoolSize=0 and a single‑thread executor used by DiskLruCache.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaThreadPoolExecutorServiceThreadPoolExecutor
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.