How to set thread pool parameters? Meituan gave an answer that shocked the interviewer.

Foreword: I once pride myself on knowing the thread pool, but after reading a technical article from Meituan, I realized that the original thread pool parameters can also be adjusted dynamically.

Without good skills, he left tears without technology while standing on the shoulders of the giant Meituan to write this article, supplementing and recording his own views.

Share with everyone, I hope it can help you.

Desolate Walking

Hello everyone, this is why, a good man in Sichuan.

Today should have been the day when the Wuhan Marathon started shooting, so let ’s talk a few words about the marathon.

The picture above was taken when I ran the 2019 Chengdu Marathon. A pair of twins accompanied the 80-year-old father to run the full marathon.

The old man in the picture is named Luo Guangde, and his life before the age of 75 is no different from other old men.

But after the influence of his son, he started to run when he was 75 years old. He has never stopped, he has completed five of the world's six major marathons (New York, London, Berlin, Chicago, Tokyo, Boston).

Originally planned to stand on the track of the Boston Marathon in April this year to complete the final challenge.

After the completion, he was the first Grand Slam runner in the world Chinese age group to complete the World Six Marathon.

However, due to the epidemic, the Boston Marathon was postponed. But it doesn't matter. I believe in the attachment of the old man, and I also believe that he will be the first person.

He said: "Life is not too late to start, the key is to act. Many young friends now lack exercise and have poor schedules. I hope the young people will act, I can run at 80, can't you run ?"

I said before that you can see a lot of interesting and moving pictures on the track. I like to run a marathon because it always gives me positive energy after the run.

Life requires a marathon. You can be late, but you cannot be absent.

Okay, back to the article.

Classic interview questions

This article still circumvents the few questions left in the third original article I wrote "Some Threads It Dies, So It Turns Into An Interview Question" :

Hey, go around, stop and go. Heaven has a good reincarnation, who will the sky spare?

In this article, I mainly answer the question thrown above: How did you get the values ​​of these parameters?

To answer this question, we must first talk about what these parameters are , please see the screenshot:

In fact, the official notes are very clear. You must combine English when you read the article , because English is written by Doug Lea (author) himself, and expresses the author's own accurate ideas.

Do n’t guess it?

1.corePoolSize:the number of threads to keep in the pool, even if they are idle, unless {@code allowCoreThreadTimeOut} is set

(The number of core threads: regardless of whether they are idle after they are created. The thread pool needs to maintain 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: A maximum of maximumPoolSize threads are allowed in the thread pool.)

3.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。

(Survival time: If after the keepAliveTime time, the thread exceeding the number of core threads has not received a new task, it will be recycled.)

4.unit:the time unit for the {@code keepAliveTime} argument

(The time 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。

(Store the queue of tasks to be executed: When the number of submitted tasks exceeds the number of core threads, the submitted tasks are stored here. It is only used to store the Runnable tasks submitted by the execute method. So don't translate it into a work queue here Okay, okay? Do n’t dig yourself.)

6.threadFactory:the factory to use when the executor creates a new thread。

(Thread engineering: used to create a thread factory. For example, you can customize the thread name. When you analyze the virtual machine stack, you can know where the thread came from by looking at the name, and it will not be forced.)

7.handler :the handler to use when execution is blocked because the thread bounds and queue capacities are reached。

(Rejection strategy: When the queue is full of tasks and the threads with the largest number of threads are all working, then the thread pool of tasks that continue to be submitted cannot be processed. What kind of rejection strategy should be implemented.)

7 parameters are introduced, I hope that when the interviewer asks you which parameters can be specified in the custom thread pool, you can answer it.

Of course, you can't memorize it by rote, so the answer is stumbling, like endorsement. It ’s better not to answer anything to me: let me give you an example, how many workers there were in the beginning ...

It is not necessary, really, just answer the name and meaning of each parameter directly. If you are awesome, you can speak English to me, and I can understand it.

Everyone understands this stuff, but it is not abstract. What are you doing with that example? Delay time?

The interview requires that you answer the questions as concisely and accurately as possible, and do n’t let the interviewer refine your keywords in your long answer.

First, the interviewer has a poor interview experience . After the interview, the interviewer often emphasizes his interview experience. Dear friends, you are worried. Your interview experience is not good. When you go back to a vomit and tell you to enter the next round of interviews, most people come without a shy face. The interviewer's experience is not good, then you really have no next round.

The second is that the interviewer has a certain time limit for the interview. The limited interview time, the front is too long, and there are fewer questions that can ask you . There are fewer questions to ask. When the interviewer wrote the score sheet, I thought, I rely on, there are still many questions I did n’t ask, and I do n’t know if this kid can answer it. Forget it, do n’t enter the next round.

Well, well, I exposed a few interview tips without further care.

Among the above 7 parameters, the main parameters we need to care about are:  corePoolSize, maximumPoolSize, workQueue (queue length) .

Therefore, the text mainly discusses this issue:

How to set corePoolSize, maximumPoolSize, workQueue (queue length) when we customize the thread pool?

Do you think I want to tell you about IO-intensive tasks or CPU-intensive tasks?

No, the good thing is to make the interviewer's eyes bright, the tiger's body startled, and the answer directly called cowhide. Do not lie to you.

Meituan Sao operation

How did the tiger's body shock?

Because I saw an article published by the technical team of Meituan: "The Implementation Principle of Java Thread Pool and Its Practice in Meituan Business"

The first time I saw this article, I was so impressed. When I saw this Sao operation by Meituan, I really called the cowhide.

(Hey, I still see too few myself.)

This article is very well written and very comprehensive. For example, the thread execution process I mentioned earlier, it is equipped with a picture, a picture is worth a thousand words:

The list of blocked queue members, at a glance:

The first part is some basic knowledge, and the second half of the article only throws a practical problem:

线程池使用面临的核心的问题在于:线程池的参数并不好配置。


一方面线程池的运行机制不是很好理解,配置合理需要强依赖开发人员的个人经验和知识;


另一方面,线程池执行的情况和任务类型相关性较大,IO密集型和CPU密集型的任务运行起来的情况差异非常大。


这导致业界并没有一些成熟的经验策略帮助开发人员参考。

What is the corresponding solution given by Meituan?

Thread pool parameters are dynamic.

尽管经过谨慎的评估,仍然不能够保证一次计算出来合适的参数,那么我们是否可以将修改线程池参数的成本降下来,这样至少可以发生故障的时候可以快速调整从而缩短故障恢复的时间呢?


基于这个思考,我们是否可以将线程池的参数从代码中迁移到分布式配置中心上,实现线程池参数可动态配置和即时生效,线程池参数动态化前后的参数修改流程对比如下:

To be honest, when I saw this picture, I remembered that I had this idea before.

Because once I used a thread pool in a scheduled task here, but the number of core threads and queue length were set relatively large. After a certain task was triggered, a large amount of data was found and the task was submitted through the thread pool. The downstream services are called in the task, which causes the downstream service to be under too much pressure for a long time, and does not limit the current, so it affects other functions provided by it.

So I asked O & M to help me reduce the number of core threads in Apollo (configuration center) and restart the service.

At that time, I was thinking that we are using Apollo to support dynamic updates. Can I modify the thread pool dynamically?

Because I did n’t know a well-established thread pool at that time, the number of core threads and the maximum number of threads could be modified dynamically.

So the initial idea was to get a new thread pool and replace the original one after listening to the parameter change.

But the question is, how do I handle the tasks in the original thread pool after stealing the sky?

I can't wait for the tasks in the original thread pool to finish before they are replaced, because at this time the tasks must be coming over continuously.

So stuck in this place.

Ashamed to say, I have seen this source code a few times, but it is still poor, learning is not good, and no one can complain.

Dismiss a wave first

In order not to waste your time, first check whether you have the basic knowledge reserve to read this article:

First, we first customize a thread pool:

Holding this thread pool, when this thread pool is working properly, I first ask you two questions:

1. If the thread pool receives 30 time-consuming tasks, what is the state (or data) of the thread pool at this time?

2. In the case that the first 30 time-consuming tasks have not been completed, how many more tasks will trigger the rejection strategy?

In fact, this is asking you the execution flow of the thread pool. Simply put, it is:

1. When 30 time-consuming tasks are received, 10 core threads are working, and the remaining 20 are queued in the queue. This time has nothing to do with the maximum number of threads, so it has nothing to do with the thread survival time.

2. In fact, you know how many tasks this thread pool can accept at most, and you know what the answer to this question is. The above thread pool accepts at most 1000 (queue length) + 30 (maximum number of threads) = 1030 tasks. So when 30 tasks have been received, if there are 1000 more time-consuming tasks, the queue is full at this time, and the threads with the maximum number of threads are all working. At this time, the thread pool is full. Therefore, in the case that the first 30 time-consuming tasks have not yet been completed, the next 1001 tasks will trigger the thread pool's rejection strategy.

You have to understand these two questions. If you can't answer them, don't look down. You can look at you with a big face.

I suggest you give this article a thumbs up first, then go online and search for the thread pool execution process article (in fact, the article of Meituan also wrote the execution process), write a Demo to run, find out, and then look at this article.

Giant shoulder

Meituan ’s article on how to set the thread pool parameters provides a good idea and solution, showing a big and comprehensive thing.

However, there is no specific display of the details of the implementation.

So the text is bold, standing on the shoulders of giants to give some additional explanations for details.

1. The pain points of existing solutions.

2. How does dynamic update work?

3. What are the points of attention for dynamic settings?

4. How to specify the queue length dynamically?

5. What are the interview questions involved in this process?

The following is an explanation from these five points.

The pain points of existing solutions.

Most of the answers on the market today are to distinguish whether the tasks in the thread pool are IO-intensive or CPU-intensive.

If it is CPU-intensive, you can set the number of core threads to +1.

Why add one?

The reason given in the book "Concurrent Programming in Java" is: even when a computing (CPU) -intensive thread is occasionally suspended due to page faults or other reasons, this "extra" thread can ensure the CPU clock cycle Will not be wasted.

Can't you understand? It doesn't matter that I don't understand. Anyway, understand it as a backup thread.

Another small point to note here is that if more than one application is deployed on your server, you have to consider the thread pool configuration of other applications.

After precise calculation, you click and set it to the number of cores. As a result, the project is deployed and you find that there are other applications that are grabbing the CPU with you. Think about it.

What about tasks that include IO operations? This is what we care about.

The calculation method given in the book "Concurrent Programming in Java" is like this:

Ideal is full, the reality is very skinny.

I previously had a system that was configured according to the parameters calculated by this formula.

The result was not good, and even the downstream system could not bear it directly.

How to say this thing, still have to remember, useful in interviews. In a real scene, only one reference value can be obtained. Based on this reference value, adjustments are made.

Let's take another look at the list of existing solutions investigated by the article by Meituan:

The first is what we said above, and it deviates from the actual business scenario.

The second is set to 2 * CPU cores, which is a bit like treating all tasks as IO-intensive. And there is generally more than one custom thread pool in a project? For example, there is a thread pool dedicated to processing data upload, and a thread pool dedicated to processing query requests, so as to do a simple thread isolation. But if they are all configured with such parameters, it is obviously unreasonable.

Not to mention the third one, ideal state. The flow is unlikely to be so balanced. Take Meituan, for example, can the flow at 3 or 4 in the afternoon be compared with the flow at lunch around 12?

Based on the pain points of the above solutions, Meituan gave a dynamic configuration solution.

How does dynamic update work?

Let's start with a dynamically updated code example:

The above program customizes a thread pool with a core thread count of 2, a maximum thread count of 5, and a queue length of 10.

Then it was stuffed with 15 tasks that took 10 seconds, letting all its 5 largest threads work, and the queue length was all 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.

Coupled with the maximum number of 5 threads being executed, it takes 3 10 seconds or 30 seconds to execute all 15 tasks.

At this time, if we change the number of core threads and the maximum number of threads to 10.

Then 10 tasks will be directly taken over by the 10 maximum number of threads, and will be processed in 10 seconds.

The remaining 5 tasks will be completed in 10 seconds.

Therefore, 15 task executions require 2 10 seconds or 20 seconds to complete processing.

Take a look at the print log of the above program:

The effect is achieved, let me first look at the principle.

First look at the setCorePoolSize method:

This method is also explained in the article by Meituan:

After the thread pool consumer calls this method to set corePoolSize during runtime, the thread pool will directly overwrite the original corePoolSize value, and adopt different processing strategies based on the comparison between the current value and the original value.

For the case where the current value is less than the current number of worker threads, it indicates that there are redundant worker threads. At this time, an interrupt request will be initiated to the current idle worker thread to realize recycling, and the redundant workers will also be recycled at the next idle.

For the current value is greater than the original value and there are tasks to be executed in the current queue, the thread pool will create a new worker thread to execute the queue task, the specific process of setCorePoolSize is as follows:

After reading the article from Meituan, I went to see the setCorePoolSize method of Spring's ThreadPoolTaskExecutor class (which is a layer of packaging for JDK ThreadPoolExecutor, which can be understood as the decorator mode): The comments are clearly written and can be found in Modify this parameter when the thread pool is running.

Moreover, if you try the source code of the JDK again, the source code also reflects the modified meaning. The two values ​​are used as the difference, but the original value is 0 when it is set for the first time.

Hey, there was no detailed study at the time, and I hated not being careful when looking at the source code.

Then look at the source code of setMaximumPoolSize:

This place is very simple, the logic is not too complicated.

1. The first is the parameter legality check.

2. Then overwrite the original value with the value passed in.

3. Determine whether the worker thread is greater than the maximum number of threads, and if it is greater, initiate an interrupt request to the idle thread.

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 of attention for dynamic settings?

During the adjustment, the number of core threads may become invalid after adjustment, such as the following:

The number of core threads before the change is 2, the maximum number of threads is 5, and we dynamically modify the number of core threads to 10.

However, it can be seen from the log that after the modification, the number of core threads has indeed become 10, but the number of active threads is still 5.

And I called the prestartCoreThread method. This method is well-known. You also know that it is to start all the core threads. There is no problem that the thread is not created.

Why is this?

There is no secret under the source code , let me 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 number of threads, the number of worker threads is reduced by one, and then null is returned.

Therefore, the actual process in this place should be: Create a new worker thread worker, and then increase the number of worker threads by one. Run the created worker worker to start acquiring task. The number of worker threads is greater than the maximum number of threads, and the number of worker threads is reduced by one. Return null, that is, no task is obtained. Clean up the task and the process ends.

This one plus one minus, so the number of worker threads that are actually performing tasks has not changed, that is, the maximum number of threads.

How to solve this problem?

The answer is ready.

When setting the number of core threads, just set the maximum number of threads at the same time. In fact, you can set both to the same value:

In this way, the number of active threads can be increased normally.

Some friends will ask: If the value of the number of active threads is too large after the adjustment, is it not necessary for us to manually adjust the value to a lower value during the low business period?

If it doesn't exist, do you remember the comments before introducing the meaning of the corePoolSize parameter:

When the allowCoreThreadTimeOut parameter is set to true, the core thread will also be recycled after idle keepAliveTime, which is equivalent to the thread pool automatically modifying it for you.

How to specify the queue length dynamically?

The dynamic setting of the maximum number of threads and the number of core threads was introduced earlier, but did you find that there is no set method to set the queue length?

Some small ghosts say that they should get the Queue object first and then take a look?

Still not, how can this be adjusted?

First let's see why there is no set method for queue length:

Because the capacity of the queue is modified by final.

But the article from Meituan clearly stated that they also support the dynamic adjustment of the queue:

But there is no detailed explanation, but don't worry, then read the following content to find that they have a queue named ResizableCapacityLinkedBlockIngQueue:

Obviously, this is a custom queue.

We can also customize a queue according to this idea, so that it can modify the Capacity parameter.

It is also very convenient to operate. Paste a copy of LinkedBlockingQueue, modify the name, and then remove the final modifier of the Capacity parameter, and provide its corresponding get / set method.

Then replace the original queue in the program:

Run to see the effect:

It can be seen that the queue size has indeed changed from 10 to 100, and the queue usage has dropped from 100% to 9%.

I went to see the comment below from the article by Meituan. One comment looks like this:

Sure enough, I did not expect.

What are the interview questions involved in this process?

Question 1: Are there any threads in the thread pool after it is created? If not, do you know of any way to warm up the thread pool?

If no task comes over after the thread pool is created, there will be no threads in it. If you need to warm up, you can call the following two methods:

Start all:

Only one is started:

Question 2: Will the number of core threads be recycled? What settings are required?

The number of core threads is not recycled by default. If you need to recycle the number of core threads, you need to call the following method:

allowCoreThreadTimeOut This value defaults to false.

One last word (for attention)

Like it, Zhou is more tired, don't fuck me, you need a little positive feedback.

If you find something wrong, since there is no message function in this number, please add me to WeChat to point it out to me, and I will modify it. (I have this sentence in every technical article, I mean it seriously.)

Thank you for reading, I stick to the original , very welcome and thank you for your attention.

I ’m a technical person. I ’m not a big guy, but I ’m a nice and warm Sichuanese guy who likes to share.

Welcome to pay attention to the public number [why technology] and insist on exporting originals. Share technology, taste life, and wish you and us progress together.

Guess you like

Origin www.cnblogs.com/thisiswhy/p/12690630.html