Avoid Common ThreadPool Pitfalls in Java: Best Practices and Gotchas
This article examines frequent mistakes when using Java thread pools—such as improper creation, unreasonable parameters, unreleased local pools, and unsafe concurrent operations—and provides detailed analysis, configuration guidelines, and best‑practice recommendations to prevent memory leaks, deadlocks, and performance issues.
Preface
Multithreading is widely used to fully utilize server resources and improve processing speed, and pooling techniques are commonly employed to avoid frequent thread creation and destruction.
This article consolidates coding standards and development experience to highlight pitfalls in multithreaded development, such as ignoring thread‑pool parameters, thread safety, deadlocks, and other risks, and performs root‑cause analysis to help avoid recurring traps.
What are the common multithreading pitfalls?
1. Incorrect thread‑pool creation
Thread resources must be provided through a thread pool; explicit thread creation is prohibited by JD.com Java coding standards. Various creation methods exist (ThreadPoolExecutor, ScheduledThreadPoolExecutor, ForkJoinPool, Executors, Spring's ThreadPoolTaskExecutor), but misuse can cause problems.
Executors' convenience methods (newFixedThreadPool, newScheduledThreadPool, newSingleThreadExecutor, newCachedThreadPool) all rely on ThreadPoolExecutor, which can be risky if the implementation details are not understood.
Issues:
FixedThreadPool and SingleThreadPool allow an unbounded task queue (Integer.MAX_VALUE), potentially causing OOM.
CachedThreadPool and ScheduledThreadPool allow an unbounded number of threads (Integer.MAX_VALUE), also risking OOM.
2. Unreasonable thread‑pool parameters
The core parameters of ThreadPoolExecutor should be configured according to the scenario:
Parameter
Meaning
Description
corePoolSize
Core thread count
Number of threads kept alive even when idle, unless allowCoreThreadTimeOut is set.
maximumPoolSize
Maximum thread count
Maximum number of threads the pool can create.
keepAliveTime
Keep‑alive time
Maximum time that excess idle threads wait for new tasks before terminating.
unit
Time unit
Unit of keepAliveTime.
workQueue
Task queue
Queue that holds Runnable tasks submitted via execute().
threadFactory
Thread factory
Factory used to create new threads.
handler
Rejection policy
Handler invoked when task submission is blocked due to full pool and queue.
When choosing a work queue, consider bounded vs. unbounded queues and how rejected tasks are handled.
Rejection policies
Policy
Description
AbortPolicy
Throws RejectedExecutionException, aborting the current flow.
CallerRunsPolicy
The submitting thread runs the task if the pool is not shut down.
DiscardOldestPolicy
Discards the oldest task in the queue and retries execution.
DiscardPolicy
Silently discards the task without any action.
Custom policies should be selected based on the specific framework (Dubbo, JSF, Netty, ActiveMQ, etc.).
3. Not destroying local thread pools
Local thread pools should be shut down after use. Two ways:
Method
Description
shutdown
Gracefully shuts down: no new tasks, but existing tasks complete.
shutdownNow
Forcibly shuts down: interrupts all threads and returns pending tasks.
GC considerations:
CachedThreadPool: no core threads; after references disappear and threads become idle, GC can reclaim the pool.
FixedThreadPool: core threads remain alive by default, preventing GC reclamation and causing memory leaks.
SingleThreadExecutor: wrapped in FinalizableDelegatedExecutorService; when external references disappear, finalize() triggers shutdown, allowing GC.
4. Low startup efficiency
Core threads are created lazily. Use ThreadPoolExecutor.prestartCoreThread() or prestartAllCoreThreads() to start them proactively.
5. Improper use of shared thread pools
Submitting async tasks without specifying a pool can let non‑core business consume core resources, leading to performance degradation. Common scenarios include:
CompletableFuture without an explicit executor uses ForkJoinPool.commonPool().
Parallel Stream also uses ForkJoinPool.commonPool() but the main thread participates.
@Async without an executor may fall back to SimpleAsyncTaskExecutor (creates a new thread per task) or ThreadPoolTaskExecutor with default large capacities, which can cause OOM.
6. Unreasonable main‑thread wait times
Avoid blocking calls without timeouts such as CompletableFuture.join() or Future.get(). Use get(timeout, TimeUnit) carefully, as sequential timeouts can accumulate.
7. Ignoring sub‑thread timeouts
Even if Future.get() times out, the sub‑thread continues running unless explicitly cancelled. Use future.cancel(true) and ensure tasks respect interruption.
8. Thread‑unsafe operations
Replace non‑thread‑safe structures (e.g., HashMap → ConcurrentHashMap, StringBuilder → StringBuffer) when accessed concurrently.
9. Not propagating thread‑local variables
ThreadLocal values are not automatically transferred to worker threads. Use libraries like Transmittable‑Thread‑Local for proper propagation.
10. Increased deadlock risk
Concurrent database operations (e.g., MySQL RR isolation with gap locks) can cause deadlocks, especially when updates target non‑existent rows.
11. Ignoring request overload
Even with well‑tuned thread pools, increased downstream load (DB connections, RPC calls) must be considered to avoid cascading performance issues.
References
[1] http://www.kailing.pub/article/index/arcid/255.html
[2] https://blog.csdn.net/sinat_15946141/article/details/107951917
[3] https://segmentfault.com/a/1190000016461183?utm_source=tag-newest
[4] https://blog.csdn.net/v123411739/article/details/106609583/
[5] https://blog.csdn.net/whzhaochao/article/details/126032116
[6] https://www.kuangstudy.com/bbs/1407940516238090242
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.
JD Cloud Developers
JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.
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.
