Mastering Java Thread Pools: Boost Performance and Avoid Common Pitfalls
Learn how Java thread pools work, why uncontrolled thread creation harms performance, and how to use the JDK Executor framework—including key methods, common configurations, and pitfalls like silent errors with submit—to build efficient, scalable concurrent applications.
Thread Reuse: Thread Pool
First, an example: suppose a system processes 50,000 records per second in two steps—storing to a database and performing business analysis. Creating a new thread for each batch would quickly accumulate many threads, consuming CPU and memory, possibly causing OOM or heavy GC pressure.
Although starting a thread with start() creates a thread that is reclaimed after run() finishes, the processing speed may not meet requirements, leading to an ever‑growing number of threads and resource waste.
Uncontrolled use of multithreading can degrade system performance despite its potential to improve throughput.
Using a thread pool limits the maximum number of active threads; excess tasks wait in a queue, preventing resource exhaustion.
Although a thread is lightweight, its creation and destruction still incur overhead; for trivial tasks, the cost may outweigh the work.
What Is a Thread Pool?
To avoid frequent thread creation and destruction, threads can be reused—similar to a database connection pool.
A thread pool maintains a set of active threads with a maximum limit; after a task finishes, the thread is returned to the pool for reuse.
JDK Support for Thread Pools
JDK provides the Executor framework
The Executor framework manages threads and essentially implements a thread pool. The main interfaces and classes are:
The ExecutorService interface is defined as:
The Executors utility class provides common factory methods:
Common pool types include:
newFixedThreadPool – fixed number of threads.
newSingleThreadExecutor – a single worker thread.
newCachedThreadPool – dynamically adjusts thread count.
newSingleThreadScheduledExecutor – like single thread executor with scheduling.
newScheduledThreadPool – scheduled executor with configurable thread count.
All these methods ultimately create a ThreadPoolExecutor with default parameters.
The constructor of ThreadPoolExecutor is:
Parameter meanings are illustrated in the following diagram:
Executor Framework Examples
Example 1:
submit(Runnable task) submits a task.
Static analysis tools (e.g., Alibaba Java Coding Guidelines plugin) may flag issues.
Plugin reference: https://github.com/alibaba/p3c
Example 2:
Or alternative formatting:
Example 3: Custom ThreadFactory and rejection policy.
More code examples: https://gitee.com/xuliugen/codes/ta5dbsge0kvhy62qu8li157
Pitfalls of Using submit()
Consider the following code:
Running it produces four results instead of five because an exception at i=0 (division by zero) is silently swallowed.
When using submit(Runnable task), exceptions are caught internally and not printed. To see errors, you can:
Replace submit() with execute().
Use a Future to retrieve the result and catch exceptions.
Result after using execute():
Result using Future:
Note: View the original source for full code.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
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.
