preamble
Thread pooling is a powerful tool for handling multithreading in Java, but it's more than just a "just use it and be done with it" tool.
Many partners have stepped on many potholes when using thread pools because of misconfiguration or ignoring details.
Today, I'd like to talk to you about the 10 pitfalls in thread pooling and how to avoid them, hopefully it will be helpful to you.
1. Use Executors directly to create the thread pool.
Many beginners create thread pools by directly using theExecutors
Shortcuts provided:
ExecutorService executor = (10);
What's the problem?
-
unbounded queue:
newFixedThreadPool
The queue used isLinkedBlockingQueue
, it is an unbounded queue and task stacking may lead to memory overflow. -
Infinite thread growth:
newCachedThreadPool
It creates unlimited threads, which may exhaust system resources when the number of tasks spikes.
Example: Risk of Memory Overflow
ExecutorService executor = (2);
for (int i = 0; i < 1000000; i++) {
(() -> {
try {
(1000);
} catch (InterruptedException e) {
();
}
});
}
The number of tasks is much larger than the number of threads, leading to infinite stacking of tasks in the queue, which may eventually lead to aOutOfMemoryError
。
method settle an issue
utilizationThreadPoolExecutor
and explicitly specify the parameters:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
4,
60L,
,
new ArrayBlockingQueue<>(100), // bounded queue
new () // rejection strategy
);
2. Misconfigured number of threads
Many people configure thread pool parameters randomly, such as 10 core threads and 100 max threads, which seems fine, but this can lead to performance problems or wasted resources.
Example: Thread overload due to misconfiguration
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, // Number of core threads
100, // Maximum number of threads
60L,
,
new ArrayBlockingQueue<>(10)
);
for (int i = 0; i < 1000; i++) {
(() -> {
try {
(5000); // Simulating time-consuming tasks
} catch (InterruptedException e) {
();
}
});
}
This configuration creates a large number of threads and system resources are exhausted when tasks surge.
The right way to configure
Choose a reasonable number of threads based on the type of task:
-
CPU-intensive: The number of threads is recommended to be set to
CPU cores + 1
。 -
IO Intensive: The number of threads is recommended to be set to
2 * CPU cores
。
Example:
int cpuCores = ().availableProcessors();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
cpuCores + 1,
cpuCores + 1,
60L,
,
new ArrayBlockingQueue<>(50)
);
3. Ignoring the selection of task queues
Task queues directly affect the behavior of the thread pool. If you choose the wrong type of queue, it can cause a lot of hidden problems.
Common Queue Pitfalls
- unbounded queue: Tasks stack up infinitely.
- bounded queue: A full queue triggers the reject policy.
- Priority Queue: It can easily lead to frequent preemption of low-priority tasks by high-priority tasks.
Example: Task stacking causes problems
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
4,
60L,
,
new LinkedBlockingQueue<>()
);
for (int i = 0; i < 100000; i++) {
(() -> (().getName()));
}
Improved methodology: Use bounded queues to avoid infinite stacking of tasks.
new ArrayBlockingQueue<>(100);
4. Forgetting to close the thread pool
Some of you guys forget to call the thread pool after using theshutdown()
, causing the program to fail to exit properly.
Example: Thread pool not closed
ExecutorService executor = (5);
(() -> ("Task execution in progress...")) ;
// The thread pool is not closed, the program keeps running
The right way to close
();
try {
if (!(60, )) {
();
}
} catch (InterruptedException e) {
();
}
5. Ignoring rejection tactics
When the task queue is full, the thread pool triggers the denial policy, and many people are unaware of the default policy (AbortPolicy
) will just throw an exception.
Example: Task rejected
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
1,
60L,
,
new ArrayBlockingQueue<>(2),
new () // default policy
);
for (int i = 0; i < 10; i++) {
(() -> ("mandates"));
}
The fourth task throws theRejectedExecutionException
。
Improvement: choosing the right strategy
-
CallerRunsPolicy
: The thread that submitted the task executes it itself. -
DiscardPolicy
: Discard the new task directly. -
DiscardOldestPolicy
: Discard the oldest tasks.
6. Exceptions not addressed in the mandate
When a task in the thread pool throws an exception, the thread pool doesn't throw it directly, causing many problems to be overlooked.
Example: Exception Ignored
(() -> {
throw new RuntimeException("mission anomaly");
});
cure
- Catch task internal exceptions:
(() -> {
try {
throw new RuntimeException("mission anomaly");
} catch (Exception e) {
("Catching exceptions:" + ());
}
});
- Custom thread factories:
ThreadFactory factory = r -> {
Thread t = new Thread(r);
((thread, e) -> {
("threading exception:" + ());
});
return t;
};
I recently spent the last year or so putting together my years ofTechnical Growth PathIt's all sunk in and is well worth a look.
Contains:real-world project, stepping pit sharing, source code interpretation, learning route, system design, technology selection, underlying principles, high frequency interview questions, there is everything inside.
7. Blocking tasks from occupying the thread pool
If the tasks in the thread pool are blocking (e.g., file reads and writes, network requests), the core threads will be full, affecting performance.
Example: Blocking Tasks Drag Down the Thread Pool
(() -> {
(10000); // Simulate a blocking task
}).
Improved methodology
- Reduce the blocking time of tasks.
- Increase the number of core threads.
- Use asynchronous non-blocking methods (e.g. NIO).
8. Abuse of the thread pool
Thread pools are not foolproof, and certain scenarios use directnew Thread()
Simpler.
Example: Overuse of Thread Pools
A simple short-term task:
ExecutorService executor = ();
(() -> ("Execute task"));.
();
In this case, it's rather complicated to use a thread pool.
Modalities for improvement
new Thread(() -> ("Execute task")).start();
9. Unmonitored thread pool status
Many people use thread pools and then don't monitor their status, leading to ignored problems with task stacking and thread exhaustion.
Example: Monitoring Thread Pool Status
("Number of Core Threads: " + ());
("Queue size: " + ().size());
("Number of completed tasks:" + ()); ("Number of tasks completed:" + ()).
Combined with monitoring tools (e.g. JMX, Prometheus), real-time monitoring is realized.
10. Dynamic adjustment of thread pool parameters
Some people ignore the need for parameter tuning during thread pool design, leading to difficulties in optimizing performance at a later stage.
Example: Dynamically adjusting the number of core threads
(20);
(50);
Real-time adjustment of thread pool parameters, can adapt to dynamic changes in business.
summarize
Thread pools are powerful tools, but they are also very easy to step into if we don't use them well in our daily work.
In this article, through actual code examples, we can clearly see what the problem with thread pooling is and how to improve it.
Hopefully, this content will help you avoid stepping on potholes and write quality thread pool code!
Thread pool used well, the efficiency of the bar; used poorly, the program crashes every day!
One final note (ask for attention, don't patronize me)
If this article is helpful to you, or inspired, help pay attention to my eponymous public number: Su San said technology, your support is my biggest motivation to keep writing.
Ask for a one-click trifecta: like, retweet, and watch at.
Concerned about the public number: [Su San said technology], in the public number reply: into the big factory, you can get free access to my recent organization of 100,000 words of the interview dictionary, a lot of partners rely on this dictionary to get a number of big factory offers.