I used to boast that I knew the thread pool like the back of my hand, but after reading a technical article by Meituan, I realized that the parameters of the thread pool can be adjusted dynamically.
I am not good at studying art, and I left tears for not having skills. I wrote this article standing on meituan’s shoulders to supplement and record my own views.
I hope I can help you.
Drought cavity be out of tune
First of all, or the characteristics of this number, cavity out of the board link. Hello, I’m Why, a good man from Sichuan.
Today is supposed to be the day when the Wuhan Marathon starts, so let’s talk about the marathon.
The picture above was taken when I ran the chengdu Marathon in 2019. It shows a pair of twins running a full marathon with their 80-year-old father.
The old man in the picture is Luo Guangde, and his life before the age of 75 was just like any other old man’s.
But through the influence of his son, he took up running at the age of 75. He has never stopped, and has completed five of the world’s six biggest marathons (New York, London, Berlin, Chicago, Tokyo and Boston).
I was going to run the Boston Marathon in April to complete the final challenge.
When he finished, he was the first Grand Slam runner in his Chinese age group to complete six major marathons in the world.
But the Boston Marathon was postponed because of the pandemic. But it doesn’t matter. I believe in pops persistence, and I believe he’ll be the first.
“It’s never too late to start in life,” he said. “The key is getting started. I hope young people can take action. I can run at the age of 80. Can’t you run?”
As I said before, you can see a lot of interesting and moving pictures on the track. I love running marathons because it always gives me a burst of positive energy afterwards.
Life needs a marathon. You can be late, but you can’t miss it.
All right, back to the article.
Classic Interview questions
This article rewinds some of the questions left over from my third original article, “Some threads Die and it becomes an interview question” :
Ah, round and round, stop and go. Heaven good reincarnation, heaven rao who?
In this article I will answer the question posed above: How do you get the values of these parameters?
To answer this question, we need to talk about what these parameters are. Here’s a screenshot:
In fact, the official notes are very clear. You must read the article in English, because the English is written by Doug Lea himself, expressing his own accurate ideas.
Don’t try to guess, okay?
1. CorePoolSize: the number of threads to keep in the pool, even if they are idle, unless {@code allowCoreThreadTimeOut} is set
Core thread size: whether or not they are idle after creation. Thread pools need to hold the number of corePoolSize threads unless allowCoreThreadTimeOut is set.
2. MaximumPoolSize: The maximum number of threads to allow in the pool
(Maximum number of threads: Maximum number of maximumPoolSize threads can be created in the thread pool.)
3. The keepAliveTime: when the number of threads is greater than the core, This is the maximum time that excess idle threads will wait for new tasks before terminating.
(keepAliveTime: If after keepAliveTime, the number of threads exceeding the core has not received a new task, then reclaim.)
4. Unit: The time unit for the {@code keepAliveTime} argument
(keepAliveTime Unit of keepAliveTime.)
5. WorkQueue: the queue to use for holding tasks before they are executed. This queue will hold only the {@code Runnable} tasks Submitted by the {@code execute} method
Queue for tasks to be executed: When the number of submitted tasks exceeds the size of the core thread, the submitted tasks are stored here. It is only used to hold Runnable tasks submitted by the execute method. So don’t translate this as a work queue, okay? Don’t dig a hole for yourself.
ThreadFactory: The factory to use when the executor creates a new thread
Thread project: Used to create thread factories. For example, the thread name can be customized so that when analyzing the virtual stack, you can look at the name and know where the thread came from without being confused.
7. The handler to use when execution is blocked because the thread bounds and queue are reached.
(Reject policy: when the queue is full of tasks and the threads with the maximum number of threads are working, the thread pool of further submitted tasks can not handle it, how to implement the reject policy.)
With 7 parameters covered, I hope you can answer when the interviewer asks you what parameters can be specified for a custom thread pool.
Of course, can’t memorize, so answer up stumbling, like in endorsement. Also had better not answer to me what: I give you cite an example, be at the beginning have how many how many workers….
No need, really, just answer the name and meaning of each parameter, or you can speak English to me if it’s cool, I can understand it.
It’s not abstract. Why would you use that example? Procrastinating?
The job interview is to answer questions as succinctly and accurately as possible, and don’t ask the interviewer to extract keywords from your long answers.
One is that the interviewer’s interview experience is not good. After an interview, it is often the candidate who emphasizes his or her interview experience. Friend, you worry too much, you interview experience is not good, go back to ridicule, call you into the next round of interview, most people are not shy to come. If the interviewer has a bad experience, you really don’t get another round.
Second, the interviewer interview is a certain time limit, limited interview time, the front is too wordy, can ask you fewer questions. Ask the question is less, the interviewer writes the score form of time think, I depend, still have a lot of questions didn’t ask, also don’t know this boy can answer come up, calculate, don’t enter next round.
Well well, a heart also exposed a few interview tips, digs away, come back.
CorePoolSize, maximumPoolSize, and workQueue are the main parameters we care about.
Therefore, the text mainly discusses this issue:
How to set corePoolSize, maximumPoolSize, and workQueue when customizing thread pools?
You think I’m going to tell you to divide IO intensive tasks into CPU intensive tasks?
No, it’s an answer that will knock the interviewer’s socks off. Don’t cheat you.
Meituansao operation
How did that happen?
Because I saw an article published by The Technical team of Meituan: implementation Principle of Java Thread Pool and Its Practice in Meituan Business
When I saw this article for the first time, I was really a flash in the eye, and I really called a cow’s skin when I saw this SAO operation of Meituan.
(Alas, I still see too little of myself.)
This article is well written and comprehensive, like the thread execution process I mentioned earlier, and it comes with a picture that says a thousand words:
Blocking queue member table
Those are the basics, but the second half of the article throws up a practical question:
The core problem with thread pool usage is that thread pool parameters are not easy to configure.
On the one hand, the operation mechanism of thread pool is not well understood, and rational configuration depends on the personal experience and knowledge of developers.
On the other hand, thread pool execution is highly dependent on task type, and IO intensive and CPU intensive tasks run very differently.
Copy the code
As a result, there are no mature empirical strategies for developers to look to.
What is meituan’s corresponding solution?
Dynamic thread pool parameters.
After careful evaluation, there is no guarantee that the correct parameters will be computed at once, so can we reduce the cost of modifying thread pool parameters so that we can at least adjust quickly in the event of a failure and thus shorten the recovery time?Copy the code
Based on this consideration, can we migrate the parameters of the thread pool from the code to the distributed configuration center, so that the parameters of the thread pool can be dynamically configured and take effect immediately? The parameter modification process before and after the dynamic thread pool parameter is compared as follows:
To be honest, WHEN I saw this picture, I remembered that I had thought about it before.
Because I once the inside of the side of a project timing task using the thread pool, but the core number of threads and the queue length is set is large, one task when triggered found large quantities of data, through the thread pool to submit tasks, each task it will be called the downstream service, leading to the downstream service for a long period of time pressure is too big, no current limiting, Therefore, other functions provided by it are affected.
I asked operations to reduce the number of core threads at Apollo (configuration Center) and restart the service.
At that point I was thinking, we’re using Apollo to support dynamic updates naturally, so can I change the thread pool dynamically?
Since a constructed thread pool is not known at that time, its core thread count and maximum thread count can be dynamically modified.
So the initial idea was to listen for the parameter change and just replace it with a new thread pool.
But the question is, what do I do with the tasks in the original thread pool?
I can’t wait for tasks in the original thread pool to finish, because that’s when they’re coming in.
So he got stuck in this place.
Ashamed to say, this piece of source CODE I read a few times, but still almost heat, not fine, blame others.
First, a wave of persuasion
In order not to waste your time, check to see if you have the basics to read this article:
First, let’s customize a thread pool:
Take this thread pool, and while it’s working, LET me ask you two questions:
1. If the thread pool receives 30 time-consuming tasks, what is the state (or data) of the thread pool?
2. If the first 30 time-consuming tasks have not been completed, how many more tasks will trigger the rejection policy?
Thread pool execution: thread pool execution: thread pool execution
1. When 30 time-consuming tasks are received, 10 core threads are working, and the remaining 20 are queuing in the queue. This has nothing to do with the maximum number of threads, so it has nothing to do with thread lifetime.
2. If you know the maximum number of tasks that the thread pool can accept, you will know the answer to this question. The maximum number of tasks that the thread pool can accept is 1000 (queue length) + 30 (maximum number of threads) = 1030. So if you have 30 tasks, if you have 1000 more time-consuming tasks, then the queue is full, and the maximum number of threads are working, then the thread pool is full. Therefore, 1001 tasks will trigger the thread pool rejection policy after the first 30 time-consuming tasks have not been completed.
These two questions you have to be able to, if the answer can not come up you also don’t look down, large probability to see a face mengbi.
I suggest you give this article a thumbs up, then go to the Internet to search the thread pool execution process article (in fact, the meituan article also describes the execution process), write a Demo to run, feel clear, then read this article.
The giant shoulder
Meituan’s article provides a good idea and solution to the problem of how to set the thread pool parameters, showing a big and comprehensive thing.
However, there was no detail on how it would be implemented.
So the text dare to stand on the shoulders of giants to carry out some supplementary explanations of the details.
1. Pain points of existing solutions.
2. How does dynamic update work?
3. What are the attention points of dynamic setting?
4. How do I dynamically specify the queue length?
5. What are the interview questions involved in this process?
The following five points are explained.
Pain points of existing solutions.
Most of the answers on the market today are to distinguish between IO – and CPU-intensive tasks in a thread pool.
If CPU intensive, you can set the number of core threads to +1.
Why add one?
The reason, as presented in Java Concurrent Programming in Action, is that this “extra” thread ensures that CPU clock cycles are not wasted even when computationally intensive threads are occasionally paused due to page misses or other reasons.
Don’t get it, do you? It’s okay. I can’t read it either. Think of it as a backup thread anyway.
Another small caveat here is that if you have more than one application deployed on your server, you need to consider the thread pool configuration of other applications.
After careful calculation, you click to set the core number, and then the project is deployed, only to find that other applications are competing with you for CPU.
What about tasks that involve IO operations? That’s what we care about.
The calculation given in The book “Java Concurrent Programming in Action” is as follows:
Ideal is full, reality is very skinny.
I had a system that was configured according to this formula.
The result is not good, and even the downstream system is unbearable.
I don’t know what to say, but remember, it’s useful in interviews. You can only get a reference value in the real scene, and then adjust it based on this reference value.
Let’s look again at the list of existing solutions investigated in meituan’s article:
The first one is the deviation from the actual business scenario as described above.
The second is set to 2 CPU cores, which is a bit like treating the task as IO intensive. And isn’t there usually more than one custom thread pool in a project? For example, there are thread pools dedicated to data delivery, and thread pools dedicated to query requests to do a simple thread isolation. However, it is obviously unreasonable to use this parameter configuration.
Forget the third one. Ideal. It is impossible for the flow to be so balanced. Take Meituan for example, can the flow at 3 or 4 o ‘clock in the afternoon be compared with the flow at lunch at about 12 o ‘clock?
Based on the pain points of these solutions, Meituan presents a solution for dynamic configuration.
How does dynamic update work?
Let’s start with an example of dynamically updated code:
The above program is a custom thread pool with 2 core threads, 5 maximum threads, and 10 queue length.
Then it was stuffed with 15 tasks that took 10 seconds, so that all five of the maximum threads were working and all 10 of the queue lengths were full.
In the current situation, of the 10 in the queue, the first 5 will be executed after 10 seconds and the last 5 will be executed after 20 seconds.
Add in the five that the maximum number of threads are executing, and it takes three 10 seconds or 30 seconds for all 15 tasks to execute.
At this point, if we change both the core thread count and the maximum thread count to 10.
Then the 10 tasks will be directly taken over by the 10 maximum threads and will be processed in 10 seconds.
The remaining five tasks will be completed after 10 seconds.
So, the 15 tasks took 2 10 seconds or 20 seconds to complete.
Take a look at the print log of the program above:
It’s done. Let me see what the principle is.
First look at the setCorePoolSize method:
This method is also explained in Meituan’s article:
After the run-time thread pool consumer calls this method to set corePoolSize, the thread pool overwrites the original corePoolSize value and takes a different processing strategy based on the comparison between the current value and the original value.
If the current value is smaller than the current number of worker threads, it indicates that there are redundant worker threads. At this time, the idle worker threads will send interrupt requests to realize recycling, and the redundant workers will also be reclaimed in the next IDEL.
If the current value is greater than the original value and there is a task waiting to be executed in the current queue, the thread pool will create a new worker thread to execute the queue task. The setCorePoolSize process is as follows:
After reading meituan’s article, I looked at the setCorePoolSize method of Spring’s ThreadPoolTaskExecutor class: The comment makes it clear that this parameter can be changed while the thread pool is running.
And, you taste a JDK source code, in fact, the source code also reflects the meaning of the modification, two values to do the difference, but the first time the original value is 0.
Ah, at that time did not carefully study, hate their own look at the source code is not careful.
setMaximumPoolSize
This place is very simple, the logic is not too complicated.
1. Firstly, verify the validity of parameters.
2. Then overwrite the original value with the passed value.
3. Check whether the number of working threads is greater than the maximum number of threads. If so, send an interrupt request to idle threads.
After the analysis of the previous two methods, we know that the maximum number of threads and the number of core threads can be adjusted dynamically.
What are the points to note about dynamic Settings?
When adjusting the number of core threads, it may be invalid, such as the following:
Before changing the number of core threads was 2, the maximum number of threads was 5, and we dynamically changed the number of core threads to 10.
However, as you can see from the log, the number of core threads does change to 10, but the number of active threads is still 5.
And I’m calling the prestartCoreThread method, which is pretty obvious, as you know, to start all the core threads, so there’s no problem with threads not being created.
Why is that?
Source code under no secret, I take you to take a look:
java.util.concurrent.ThreadPoolExecutor#getTask
In this method, we can see that if the number of worker threads is greater than the maximum, we subtract the number of worker threads by one and return NULL.
So, the actual flow of this place should be: create a new worker thread, and add one to the number of worker threads. Run the created worker thread worker and start getting the task task. If the number of worker threads is greater than the maximum number, subtract one from the number of worker threads. Returns NULL, that is, no task was obtained. Clear the task, and the process ends.
One plus one minus, so the number of worker threads actually executing the task never changes, the maximum number of threads.
How to solve this problem?
The answer is already there.
When setting the number of core threads, set the maximum number of threads at the same time. You can set them to the same value:
In this way, the number of active threads can normally increase.
Some partners will ask: if after adjusting the number of active threads set the value is too large, is not the business peak period we need to manually adjust the value of a little bit smaller?
Does not exist, remember the previous comment on the meaning of the corePoolSize parameter:
When allowCoreThreadTimeOut is set to true, the core thread is reclaimed after it has been idle for keepAliveTime, which means that the thread pool has automatically changed for you.
How do I dynamically specify queue length?
The maximum number of threads and the number of core threads are set dynamically, but did you notice that there is no set method to set the queue length?
What if I get the Queue and look at it?
Or not. What can we do about it?
First let’s see why there is no set method that provides the length of the queue:
Capacity of the queue is final.
But as the Meituan article clearly states, they also support dynamic alignment of queues:
But no details, but don’t try so hard, then look at the back of the content can be found that they have a name for ResizableCapacityLinkedBlockIngQueue queue:
Obviously, this is a custom queue.
We can also customize a queue in the same way that the Capacity parameter can be modified.
It’s also easy to paste a copy of the LinkedBlockingQueue, change the name, remove the final modifier for the Capacity parameter, and provide the corresponding GET /set method.
Then replace the original queue in the program:
Run to see the effect:
You can see that the queue size did go from 10 to 100, and the queue usage went from 100% to 9%.
I later read the comments on Meituan’s article and one of them went like this:
As I expected.
What are the interview questions involved in this process?
Question 1: Are there threads in the thread pool after it is created? If not, do you know of any ways to preheat a thread pool?
When a thread pool is created, there are no threads in it if no tasks come in. The following two methods can be called if you need to warm up:
Start all:
Start only one:
Q 2: Will the number of core threads be reclaimed? What Settings are required?
The number of core threads is not reclaimed by default. To reclaim the number of core threads, call the following method:
AllowCoreThreadTimeOut This value defaults to false.
One last word (for attention)
Give me a thumbs up, zhou is more tired, don’t fuck me, need some positive feedback.
There will inevitably be mistakes. If you find mistakes, please add me to wechat to point them out to me because this number has no message function, and I will modify them. (I have this quote in every technical article, and I mean it.)
Thank you for reading, I insist on original, very welcome and thank you for your attention.
I am why Technology, a nice sichuan man who is not a big shot, but likes to share, warm and interesting.
Welcome to pay attention to the public account [WHY Technology], adhere to the output of original. Share technology, taste life, wish you and I progress together.