Avoid Using Executors for Thread Pools: Use ThreadPoolExecutor with Proper Parameters
The article explains why creating Java thread pools with the Executors factory methods can lead to unbounded queues and OOM, and demonstrates how to safely construct thread pools using ThreadPoolExecutor by configuring its seven key parameters.
Rule: Thread pools should not be created via the Executors utility class; instead they must be instantiated directly with ThreadPoolExecutor and its seven configuration parameters set appropriately.
This practice makes the pool’s runtime behavior explicit and helps avoid resource‑exhaustion problems.
Reason: The factory methods in Executors (e.g., newFixedThreadPool, newCachedThreadPool, newSingleThreadExecutor) create pools with unbounded waiting queues or unlimited thread counts, which can cause tasks to accumulate and eventually trigger an OutOfMemoryError.
Examples of the problematic factory methods:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
} public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
} public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
} public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
} public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(
new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
} public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService(
new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}In these implementations the waiting queue is unbounded (no capacity limit), and in the cached pool the maximum thread count is effectively unlimited, both of which can cause memory to fill up when tasks pile up or threads are not released.
Summary: Because the Executors factories can easily lead to OOM, developers should create thread pools explicitly with ThreadPoolExecutor, carefully configuring core pool size, maximum pool size, keep‑alive time, work queue, thread factory, and rejection handler.
JDK source version used for the examples:
java version "17.0.2" 2022-01-18 LTS
Java(TM) SE Runtime Environment (build 17.0.2+8-LTS-86)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.2+8-LTS-86, mixed mode, sharing)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.
Cognitive Technology Team
Cognitive Technology Team regularly delivers the latest IT news, original content, programming tutorials and experience sharing, with daily perks awaiting you.
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.
