Thread pool concept

0 1

Basic concepts of thread pools

Thread pool is a Thread usage pattern. Too many lines will bring scheduling overhead, which will affect cache locality and overall performance. A thread pool maintains multiple threads, waiting for the supervisor to assign tasks that can be executed concurrently. This avoids the cost of creating and destroying threads while working on short-duration tasks. (Source: Wikipedia)

Java thread pool-related classes were added in 1.5 with the package rt.jar and the package path java.util.concurrent by Doug Lea from JSR-166.

Java thread pool also follows the core design idea of thread pool, reuse threads, reduce the resource consumption of thread creation and destruction, provide a variety of thread pool implementation model, but also allow developers to customize other feature thread pool.

0 2

Java thread pool advantage

A) Reduce resource consumption and improve efficiency: reduce consumption caused by thread creation and destruction by reusing the created threads, so as to improve the overall execution efficiency.

B) Improve the management of threads: Threads are scarce resources. If they are created without limit, they will not only consume system resources, but also reduce the stability of the system. Thread pools can be used for unified allocation, tuning and monitoring.

C) Extensible development mode: In addition to the three thread pools provided by the JVM, you can customize your own thread pool by implementing the AbstractExecutorService class to support different business scenarios.

(here a lot of data is a extra: improve the response speed, namely by reusing threads to create good, without having to wait for the creation of a new thread here personally think technically, this one is not completely accords with the usage of the thread pool, and reuse the thread and the first basic consistent, so together, as for the special behind the scenes in the process of learning will explain in detail.)

0 3

Introduction to Java native thread pools

First of all, introduce the Java native three thread pool, ForkJoinPool, ThreadPoolExecutor, ScheduledThreadPoolExecutor. The UML class diagram for the three thread pools is shown below (version 1.8.0_131, on which the rest of the source code is based).

A) ForkJoinPool is A new concurrency framework introduced in Java 1.7 that takes advantage of multi-cpu, multi-core processing by forking large tasks and then joining them to A single result. A “work-stealing” mechanism has been introduced to take advantage of threads more efficiently.

B) ThreadPoolExecutor is a common thread pool in Java that provides basic thread pool functionality. Initialization passes in different types of work queues and reject policy parameters, customizing thread pools of different types and functions is the most widely used.

C) ScheduledThreadPoolExecutor can be seen from the class diagram, it inherits the ThreadPoolExecutor, and implements the ScheduledExecutorService, is extended to ThreadPoolExecutor do function, It is essentially a class that uses a thread pool to perform timed tasks. It can be used to perform asynchronous or periodic tasks after a given delay, which is more powerful than a task scheduling Timer.

Implementation principle of thread pool

Next, ThreadPoolExecutor, the basic thread pool of Java, is introduced to the working principle and implementation of the thread pool.

0 1

ThreadPoolExector class

2.1.1 Introduction to ThreadPoolExector A) The top-level interface of ThreadPoolExecutor implementation is Executor. There is only one internal method, execute(Runable), which identifies the core method of executing A task. Restrict the task type to Runable, i.e. thread interface class.

B) The ExecutorService interface extends many capabilities, such as thread pool management. And expanded the ability to execute tasks, support multiple tasks batch execution.

C) AbstractExecutorService is an abstract class for ExecutorService, which provides a basic implementation of task execution and invocation.

D) ThreadPoolExecutor is a basic implementation class of Java native thread pool. It implements various functions of thread pool, maintains blocking queue for storing tasks, worker threads for executing tasks, and manages thread pool state and task. Some extension methods are also provided. Customize features for developers.

Let’s talk about the important parameters for creating a ThreadPoolExecutor.

CorePoolSize: specifies the number of core threads in the thread pool. Queue: A blocking queue that holds tasks waiting to be executed. Such as array-based bounded ArrayBlockingQueue, linked list based unbounded LinkedBlockingQueue, PriorityBlockingQueue, etc. MaximunPoolSize: specifies the maximum number of threads in a thread pool. ThreadFactory: Factory for creating threads. You can customize the volunteer factory, control the thread name generated to assist troubleshooting. RejectedExecutionHandler: Saturation policy adopted when the queue is full and the number of threads reaches maximunPoolSize, for example, AbortPolicy. DiscardPolicy Discards the task. KeeyAliveTime: indicates the survival time. The maximum length of time that idle threads can survive if there are more threads in the current thread pool than there are core threads and they are idle. TimeUnit: indicates the TimeUnit of the keepalive time.

2.1.3 ThreadPoolExecutor. ThreadPoolExecutor Worker inner class. The Worker threads, this work implements the Runnable interface, inherited AQS class at the same time, and hold a thread thread, An initialized task firstTask. Responsible for processing tasks and maintaining the state of worker threads.

Threads are threads created from ThreadFactory when the constructor is called to perform tasks.

FirstTask is the firstTask that is passed in. If it is not empty, the thread executes the task immediately upon startup, which is the case when the core thread is created. If this value is null, then a thread needs to be created to perform the tasks in the workQueue, that is, the creation of non-core threads. After a single task is completed, the worker continues to fetch the next task in the workQueue to continue the execution.

0 2

ThreadPoolExecutor workflow

2.2.1 General Introduction

The thread pool actually builds a producer-consumer model internally, decouples threads and tasks, and does not correlate them directly, so as to buffer tasks well and reuse threads. The operation of thread pool is mainly divided into two parts: task management and thread management. The task management section acts as a producer, and when a task is submitted, the thread pool determines the subsequent execution strategy for the task. There are mainly the following types:

(1) Directly apply for the thread to execute the task;

(2) Buffer to queue for thread execution;

(3) Deny the task and execute the deny policy.

The thread management part is the consumer, which is uniformly maintained in the thread pool. According to the task request, threads are allocated. When the thread completes the task, it will continue to acquire new tasks to execute.

2.2.2 Status Change

ThreadPoolExector internally maintains the state of the thread pool and the number of threads in the pool using a variable of type CTL of AtomicInteger, i.e., three status bits and the remaining number of threads.

Each state can be transformed by calling a different method.

  1. RUNNING(-1:111) : Can accept new tasks or process tasks in a blocking queue.

  2. SHUTDOWN(0:000) : does not accept new tasks, but can process tasks in the blocking queue.

  3. STOP(1:001) : does not accept new tasks, does not process tasks in the blocking queue, and interrupts ongoing tasks.

  4. TIDYING(2:010) : Transient state, that is, all tasks are finished and there are no valid threads in the thread pool. The thread pool state will TIDYING and terminated methods will be called.

  5. TERMINATED(3:011) : TERMINATED state. State of terminated method call after completion.

2.2.3 Source code parsing -execute Execute is a method entry method for submitting tasks. According to the number of core thread pools, status of thread pools, size and maximum number of task queues, create different threads, store them in different locations, or execute rejection policies.

AddWorker addWorker is a method to addWorker threads. It encapsulates a Thread instance to maintain the execution of Worker threads through Worker internal classes, and determines whether to add corresponding tasks according to the state of the Thread pool.

2.2.5 Source code parsing -runWorker Where runWorker actually performs tasks, first performs the first task, and then continuously fetches tasks from the task queue for execution; This is also affected if shutDownNow is called by the thread pool.

By default, the poll() or take() method of the queue is determined based on the number of worker threads relative to the number of cores. KeepAliveTime is also used here.

2.2.7 Task Queue The BlockingQueue is used to store tasks. When there are idle threads in the thread pool, it goes back to the task queue and processes the task.

In multithreaded environments, it is easy to share data through queues, such as in the classic “producer” and “consumer” model.

Common centralized queues are:

Unbounded queue: Use an unbounded queue (such as LinkedBlockingQueue) to store all tasks in a blocking queue. In this way, the number of threads created will not exceed corePoolSize.

Bounded queue: Bounded queues (e.g. ArrayBlockingQueue, PriorityBlockingQueue) help prevent resource exhaustion when using limited Sizes maximumPoolQueue, and PriorityBlockingQueue can also customize the priority of tasks. However, the developer needs to adjust the queue size and thread pool size according to the actual task situation.

SynchronousQueue If you do not want tasks to wait in the queue but rather want them to be handed over directly to a worker thread, use SynchronousQueue as the wait queue. SynchronousQueue is not a true queue, but rather a mechanism for hand-over between threads. To place an element in the SynchronousQueue, another thread must be waiting to receive the element. This queue is recommended only if you are using an unbounded thread pool or if you have a saturation policy.

2.2.8 Rejection Policy

  1. CallerRunsPolicy The calling thread executes the task without abandoning it, which affects efficiency and performance.

  2. AbortPolicy throws exceptions that refuse to be executed. AbortPolicy is the default denial policy of the Java thread pool to ensure efficient execution of the thread pool as a whole.

  3. DiscardPolicy discards the task without doing any processing or throwing exceptions. Not very friendly to use.

  4. DiscardOldestPolicy If the thread pool is not closed, then eject the first of the task queues and run.

These are the four rejection policies that are provided by default. In addition, there are several other processes that are worth considering in other frameworks.

AbortPolicyWithReport This is the reject policy in Dubbo, which inherits the AbortPolicy reject policy. However, before the exception is thrown, the important parameter information of the current thread pool is printed, as well as the running status, and the stack information at this time is customized. This facilitates subsequent troubleshooting.

Application scenarios of thread pools

0 1

Thread pools in Tomcat

As an excellent Web server, Tomcat also has its own internal thread pool object to ensure its performance: Org, apache tomcat. Util. Threads. ThreadPoolExecutor inherited from Java. The util. Concurrent. ThreadPoolExecutor.

Unlike native ThreadPoolExecute, which executes a reject policy on new tasks as soon as it reaches its maximum thread. The Tomcat thread pool will try again at this point to add the task to the queue and then execute the rejection policy if it fails. Ensure maximum execution of tasks.

TaskQueue is also built into Tomcat as a cache queue for tasks. Inherits LinkedBlockingQueue but overwrites the Offer method, meaning that the current thread is larger than the core thread and the number of submitted tasks is larger than the current thread. This is mainly to control the problem of reaching the maximum number of threads without being able to create more threads when the thread queue grows indefinitely.

0 2

Thread pools in Sirector

Sirector is an event processing orchestration framework within JD, with built-in ExecutorService objects that are responsible for task assignment.

The initialized object is WorkerExecutor, which inherits from ThreadPoolExecutor and extends the Submit method to perform specific tasks orchestrated by Sirector.

WorkerExecutor has a built-in factory. The WorkerThreadFactory records the name of the current thread pool, the number of threads created by the factory, and so on.

The RejectedTaskController policy is inherited from RejectedExecutionHandler. The processing method is similar to the AbortPolicy policy. The RejectedExecutionHandler policy is similar to the RejectedExecutionHandler policy.

0 3

Thread pools in personal development

A few general conclusions can be drawn from the thread pools of the above frameworks.

If thread pools are involved in personal development, determine the type of thread pool by identifying the task scenario, whether it is an I/O intensive or CPU intensive task.

Then, according to the application scenario, whether to maximize the task execution or to ensure service performance, we can customize our own execution policy, and determine the task queue and reject policy. For details about the rejection policy, see Dubbo and print thread information for troubleshooting.

Then determine whether custom thread factories are needed. It is recommended that custom thread factories be marked when creating threads to distinguish them from system threads.

Configure reasonable thread pool parameters based on the task type. A thread pool of your own is built!

Thread pool parameter Settings

In my opinion, there is no one-size-fits-all parameter for all thread pool usage scenarios.

But there are general ideas for finding the best parameters for the current thread pool.

1. Determine the current task type, CPU intensive or I/O intensive. There’s a big difference between the two. CPU density is related to the number of CPU cores and CPU hyper-threading. I/O intensity, on the other hand, has a lot to do with the tasks that the service processes.

2. If you use thread pools in some existing technical frameworks. It is recommended to use the default parameters, such as the Tomcat default range of 25-200 and the JSF default cached thread pool of 20-200.

3. Performance tuning after service stabilization. It is necessary to perform several high-fidelity pressure tests on the service, during which the thread pool parameters are constantly controlled and adjusted, so as to obtain the optimal thread pool parameters of the current service as much as possible.

4, only the most appropriate, there is no constant, as the business continues to iterate, every once in a while to pressure test the service, adjust the corresponding parameters through the results.

Recommendations for thread pool usage

When a task is submitted to a thread pool, if the number of threads is less than corePoolSize, the pool creates a new thread and puts it into workers(a HashSet) to perform the task. The pool creates a new thread even if other free base threads can perform the task until corePoolSize is reached.

By default, the initial thread pool starts with no threads initialized. All core threads can be initialized by calling prestartAllCoreThreads.

3. If an exception is thrown when processing a task in Worker, the work thread will not continue to execute the task, but will create a new thread, which can run other tasks.

4, Do not use Executors to create a new thread pool, because many methods provided by Executors do not specify the actual core and maximum thread pool parameters, easy to happen OOM, recommend to create their own thread pool, suitable for their own is the best, at the same time, thread pool hook methods can be used to customize features.