Setting the number of threads in the thread pool too high can lead to competitive threads, and if the number of threads is set too low, the system can not make full use of computer resources. So how do you set it so that it doesn’t affect system performance?

In HotSpot VM’s threading model, Java threads are mapped one-to-one to kernel threads. When Java uses threads to execute programs, it creates a kernel thread; When the Java thread is terminated, the kernel thread is also reclaimed.

Therefore, the creation and destruction of Java threads will consume certain computer resources, thus increasing the performance overhead of the system.

In addition, creating a large number of threads can also cause performance problems for the system, as both memory and CPU resources are preempted by threads. If not handled properly, memory overflow and CPU utilization can occur.

To address both types of problems, Java provides the concept of thread pools, which can create a fixed number of threads for business scenarios where threads are frequently created, and at the bottom of the operating system, lightweight processes map these threads to the kernel.

Thread pooling improves thread reuse while fixing the maximum thread usage and preventing unlimited thread creation. When the program is to submit a task need a thread to lookup to see if there is idle thread in the thread pool, if any, are directly using the thread pool of threads in the work, if not, will go to judge whether have created the number of threads currently exceed the maximum number of threads, if not more than, is to create a new thread, such as more than, are waiting in line or directly throw an exception.

Java provides a ThreadPool framework to implement thread pooling. In order to better implement user-level thread scheduling, Java provides a set of Executor framework to help developers with multi-threaded development more effectively.

The framework included ScheduledThreadPoolExecutor and ThreadPoolExecutor two core thread pool. The former is used to execute tasks on a scheduled basis, while the latter is used to execute submitted tasks. Since the core principles of both thread pools are the same, let’s focus on how the ThreadPoolExecutor class implements thread pools.

Public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,// The maximum number of threads in the thread pool long keepAliveTime,// If the number of threads is greater than the number of core threads, the number of idle threads is BlockingQueue workQueue,// the task queue is used to store the waiting ThreadFactory ThreadFactory,// the ThreadFactory, Used to create threads, generally modrejectedexecutionHandler handler) // Reject the policy when submitting tasks

The thread pool has two thread count Settings, one for the core thread count and one for the maximum thread count.

After a thread pool is created, by default, there are no threads in the pool until a task is available to execute.

One exception is that calling the prestartAllCoreThreads() or prestartCoreThread() method allows you to pre-create a number of threads equal to the number of core threads. This method is called preheating and is often used in panic buying systems.

When the number of threads created is equal to corePoolSize, the submitted task is added to the set blocking queue. When the queue is full, threads are created to execute the task until the number of threads in the pool equals maximumPoolSize.

When the number of threads is equal to maximumPoolSize new submission task cannot be added to the waiting queue, also unable to create the non-core threads execute directly, we have nothing to set rejection policies for the thread pool, then the thread pool will be thrown RejectedExecutionException abnormalities, That is, the thread pool refuses to accept the task.

When the number of threads created in the thread pool exceeds the set corePoolSize, the thread will be reclaimed if no new tasks are assigned to it after waiting for keepAliveTime. When a thread pool collects threads, it treats so-called “core” and “non-core” threads equally, and the collection process does not stop until the number of threads in the pool equals the corePoolSize parameter.

Even the corePoolSize thread, in some non-core business thread pools, can affect the core business thread pool if the number of threads is occupied for an extended period of time, and the unassigned threads need to be reclaimed. With the allowCoreThreadTimeOut setting, thread pools are required to recycle all threads, including “core threads”, after waiting for keepAliveTime.

Counting the number of threads Generally, the types of tasks performed by multithreading can be divided into CPU intensive and I/O intensive tasks. According to different task types, we use different methods to count the number of threads.

Cpu-intensive tasks: This type of task consumes CPU resources. The number of threads can be set to N (number of CPU cores) +1. One more thread is used to prevent occasional page miss interrupts or pauses caused by other reasons. Once the task is paused, the CPU is idle, and in this case the extra thread can take full advantage of the idle CPU time.

When the number of threads is too small, a large number of requests will be blocked in the thread queue waiting for the execution thread at the same time, and the CPU is not fully utilized. When the number of threads is too large, the created execution threads compete for CPU resources at the same time, resulting in a large number of context switches, which increases the execution time of the threads and affects the overall execution efficiency.

I/ O-intensive task: A task in which the system spends most of its time processing I/O interactions, and the thread does not occupy CPU for the time it is processing I/O, so it can be handed over to another thread. Therefore, in the application of I/O intensive tasks, we can configure more threads, the specific calculation method is 2N.

In a common application scenario, we often do not encounter these two extremes. How do we set the number of thread pools for some common business operations, such as implementing a thread pool to push messages to users on a regular basis?

At this point, we can refer to the following formula to calculate the number of threads:

Number of threads =N (number of CPU cores) * (1+WT (thread wait time) /ST (thread time))

The WT/ST ratio can be viewed using VisualVM, a tool that comes with the JDK

In general, we can choose a suitable one from the two formulas “N+1” and “2N” according to our own business scenarios, calculate an approximate number of threads, and then gradually adjust the two directions of “increasing the number of threads” and “decreasing the number of threads” through the actual pressure test, and observe the overall change of processing time. Finally determine a specific number of threads.