preface

A lot of times, I want to convey the idea that you have to understand the principles before you can write code as you like. And look at the source code, it is a very important way to understand the principle.

However, before the fertilizer dynasty articles, roughly divided into three categories

  • Source code analysis, how to read the source code (refer to the Dubbo source code analysis series article)

  • How to copy a one-to-one source code (refer to the dubbo source code analysis – simple principle, and spring fusion, a than single-hand write dubbo source SPI, with git address

  • Read the source code, what problem was solved (see fat toward this operation? Analysis of why to see the source code)

The third point, I think, is particularly important. We look at the source of the purpose is to solve the problem, I think only talk about pay, do not talk about the return are playing rogue. If only to tell you to understand the principle, look at the source code, and then paste a few large sections of the source code, and then to large sections of the source code annotations. Read a large section of notes down, as if to understand, the feeling is very “substantial “.

But what we want is not this feeling of “enrichment “, but really through the source code, to solve the search can not solve the problem, only in this way. Is a harvest. If Baidu literally a search have the answer that you also look at the source code this is really loaded force

Straight into the theme

Today, in the performance group of the company’s pressure test, there is such a problem, as shown in the following figure:

At a glance, roughly the number of exceptions thrown when the Dubbo thread pool reaches its maximum number of threads. So let’s start with the basic knowledge of thread pools

Common Thread pools

  • SingleThreadExecutor: single-threaded thread pool, rarely used.

  • FixedThreadExecutor: a fixed number of thread pools

  • CachedThreadExecutor: a cache thread pool

  • ScheduledThreadExecutor: scheduled thread pool, rarely used. Well, there might be an argument here. So why does Dubbo source code use this for timed tasks? Look at the source code is the most important or to see other people’s design ideas. Dubbo was designed to rely solely on the JDK to use his scheduled tasks, and naturally chose to use the JDK’s native API to do a simple scheduled task.

Meaning of thread pool parameters and how they work

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
  / /...
}
Copy the code

Thread pools have several important parameters

CorePoolSize: The number of core threads in the thread pool

MaximumPoolSize: the maximum number of threads allowed in the thread pool

KeepAliveTime: If the current thread count > corePoolSize, the extra threads will be released after keepAliveTime

Unit: keepAliveTime Indicates the keepAliveTime unit, such as minutes or hours

WorkQueue: queue

ThreadFactory: whenever a new thread needs to be created into the thread pool, it is created through this threadFactory

Handler: That is, when the thread, queue is full, and then the strategy, such as throwing an exception, etc

So let’s assume a set of parameters to practice what this parameter means

CorePoolSize: 1 mamximumPoolSize: 3 keepAliveTime: 60s workQueue: ArrayBlockingQueue, bounded blocking queue size: 4 handler: The default strategy, threw out a ThreadPoolRejectExceptionCopy the code

1. Initially, there is a thread variable poolSize that maintains the current thread count. At this point poolSize = 0

2. Here comes a task. PoolSize(0) < corePoolSize(1)

3. Here comes a task. PoolSize(1) >= corePoolSize(1); poolSize(1) >= corePoolSize(1)

4. If the queue is also full, but poolSize < mamximumPoolSize, continue to create the thread

5. If poolSize == maximumPoolSize, then handler is executed for each task submitted, and the default is to throw an exception

PoolSize= = maximumPoolSize(3); corePoolSize=1; corePoolSize=1; corePoolSize=1; corePoolSize=1;

That’s what thread pool parameters mean and how they work

Thread pool parameter design considerations

With that in mind, let’s look at the parameter design of two common thread pools, FixedThreadExecutor and CachedThreadExecutor

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
Copy the code
public static ExecutorService newCachedThreadPool(a) {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
Copy the code

So here’s the question

1. Why are FixedThreadExecutor’s corePoolSize and mamximumPoolSize designed to be the same?

2. Why was the mamximumPoolSize of CachedThreadExecutor designed to be nearly infinite?

Type on the blackboard and underline

Or the previous sentence, we look at the source code, not a large section of the source code annotations, the most important thing is after deep thinking, understand the intent of the author’s design, that is why there are so many source code analysis articles on the market, we still have to pay attention to the fat (sell a meng)

If you have a clear understanding of the thread pool principle and parameters above, you will soon understand the design idea.

For example, the thread pool determines corePoolSize first, then workQueue, and finally mamximumPoolSize. However, LinkedBlockingQueue is an unbounded queue, so it cannot determine mamximumPoolSize In terms of mamximumPoolSize, there is no big deal

For example, let’s look at the comment for SynchronousQueue:

The size of this queue is small, and if mamximumPoolSize is not designed to be large, it is easy to throw exceptions at every turn

Suggestions on thread pool usage

We’ve got the principle, we’ve got the design idea, we’ve got the code. Light theory doesn’t work, that is, how do we use thread pools in our projects? So let’s take a look at ali’s manual, and see this compelling belief and I don’t have to say anything, okay

Dubbo thread pool

So let’s take a look at the official Dubbo documentation, which I’ve always said is the best source of learning.

Regression problems

So back to the problem we had earlier. The default thread pool for Dubbo is fixed. The default thread pool for Dubbo is fixed. The default thread pool for Dubbo is fixed Trigger handle to throw an exception. That in front of the abnormal pressure measurement problem how to come, fat toward the above analysis are lying? Fat face is also a big pig hoofs??

Straight to the source

This kind of problem.search is not going to work, because it’s not going to work. So we have to go straight to the source

@SPI("fixed"Public interface ThreadPool {/** * ThreadPool ** @param url thread parameter * @returnThread pool */ @adaptive ({Constants.THREADPOOL_KEY}) Executor getExecutor(URL URL); }Copy the code
public class FixedThreadPool implements ThreadPool {

    public Executor getExecutor(URL url) {
        String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
        int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
        int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
        return new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS, 
        		queues == 0 ? new SynchronousQueue<Runnable>() : 
        			(queues < 0 ? new LinkedBlockingQueue<Runnable>() 
        					: new LinkedBlockingQueue<Runnable>(queues)),
        		new NamedThreadFactory(name, true), newAbortPolicyWithReport(name, url)); }}Copy the code

The FixedThreadPool parameter created by Dubbo is different from that created by newFixedThreadPool. By default,Dubbo’s FixedThreadPool,maximumPoolSize = 200, is a small SynchronousQueue. So when the number of threads exceeds 200, an exception is thrown. This is consistent with the principle of our analysis above.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Once we know how the thread pool works and the root cause of the exception, we can customize the thread pool parameters according to the business characteristics to avoid the frequent occurrence of such exceptions. Or change the default thread model of Dubbo from aLL to message, etc., which needs to be combined with the actual business situation (the real cases of the company will be abstracted into simple models later to share with you).

Write in the last

Fertilizer toward is a focus on principle, source code, development skills of technical public number, number of original thematic source code analysis, real scene source code principle combat (key).Scan the following QR codePay attention to fat, let should build rocket you, no longer screw!