Talking about the ThreadPoolExecutor tool class in the Java thread pool

 

Table of contents

Constructor of ThreadPoolExecutor

 Some supplements about the thread pool

Analysis of the operating principle of the thread pool

Explanation of concepts and principles

The whole flow chart is as follows:

a little supplement


There are two main ways to create a thread pool:

  • Created through the Executor factory class, the creation method is relatively simple, but the customization ability is limited
  • Created by ThreadPoolExecutor, the creation method is more complicated, but the customization ability is strong

But we generally do not recommend using the Executor factory class to create threads.

The reasons are as follows:

Many methods provided by Executor use unbounded LinkedBlockingQueue by default. Under high load conditions, unbounded queues can easily lead to OOM—— OOM stands for "Out Of Memory", which means that memory is exhausted. This error is thrown when the JVM does not have enough memory to allocate space for the object, and the garbage collector has no space to reclaim.

Then once OOM occurs, all requests cannot be processed, which is a fatal problem. All should try to avoid the use of unbounded queues -- "that is, use Executor carefully to create threads

So let's learn more about the recommended ThreadPoolExecutor core tool class.

Constructor of ThreadPoolExecutor


The constructor of ThreadPoolExecutor is very complex, as shown in the following code, the most complete constructor has 7 parameters.
 

ThreadPoolExecutor (
            int corePoolSize,
            int maximumPoolSize,
            long keepAliveTime,
            TimeUnit unit,
            BlockingQueue<Runnable> workQueue,
            ThreadFactory threadFactory,
            RejectedExecutionHandler handler
    )

Let's take a look at what the meanings of these 7 parameters are!

corePoolSize

The number of core threads is the lowest number of threads held by the thread pool

maximumPoolSize

Maximum number of threads

keepAliveTime and unit 

We first need to know that threads are divided into two states: idle (blocking and waiting) and busy (executing tasks). When a thread is idle for a long time, and the number of threads in the thread pool is greater than the number of core threads corePoolSize, the thread will be will be destroyed and recycled

keepAlive and unit are used to measure the idle time. When the idle time is greater than keepAliveTime (unit is keepAliveTime is the time unit), and the number of threads is greater than corePoolSize, the thread will be logged off

workQueue

Work queue (a blocking queue for storing executable tasks Runnable Task)

threadFactory

Through this parameter, you can define how to create the thread yourself, for example, you can assign a meaningful name to the thread

handler

Through this parameter, you can customize the rejection strategy of the task. If the threads in the thread pool are busy and the work queue is full (the premise is that the work queue is a bounded queue), when a new task is added, Submit the task, the thread pool will refuse to accept it.

As for the rejection strategy, you can specify it through the handler parameter, and ThreadPoolExecutor provides the following four strategies.

  • CallerRunsPolicy: The thread that submits the task executes the task by itself
  • AbortPolicy: The default rejection policy will throw RejectedExecutorException—"This is a runtime exception. The editor does not force catch and is easy to ignore. Developers should use it carefully (you can define your own rejection policy)
  • DiscardPolicy: Discard tasks directly without any abnormal output
  • DiscardOldestPolicy: Discard the oldest task. In fact, it is to discard the task that entered the task queue workQueue the earliest, and then add the new task to the task queue.

 Some supplements about the thread pool

Thread is a heavyweight object, frequent creation and destruction should be avoided

At present, the design of thread pools in the industry generally adopts the producer-consumer model.


The user of the thread pool is the producer, and the thread pool itself is the consumer

For example, our task queue workQueue above (the produced Runnable tasks are placed in this queue), these tasks are executed (consumed) by threads in the thread pool

Analysis of the operating principle of the thread pool

Explanation of concepts and principles

1. Create a thread pool. When there is no task submission at this time (when the blocking queue workQueue is empty, there is no thread in the default thread pool at this time. Of course, you can also call the prestartThread method at this time to pre-create a core thread

2. When there are no threads in the thread pool or the surviving threads in the thread pool are smaller than the number of core threads corePoolSize, each time a new task is submitted, the thread pool will create a new thread to process the task just submitted (blocking queue Runnable task in workQueue).

At this time, the threads in the thread pool will always be alive, even if the thread is idle, even if the idle time exceeds keepAliveTime, the thread will not be destroyed (at this time, the threads in the thread pool are less than the number of core threads corePoolSize)    

Then at this time, the thread can only wait for when there are executable tasks (Runnable task) in the task queue, and then rewrite and start executing (become busy, before that, the thread has been blocked there——> This is why the thread pool queue uses a blocking queue)

3. When the number of threads in the thread pool is equal to the number of thread cores corePoolSize, and there is space in the task queue, when a new task is submitted, the task will be queued in the task queue (workQueue) for execution. Instead of creating a new thread to execute the task just submitted.

At this time, the thread created before will not be canceled, but will continue to get the tasks in the blocking queue. When the task in the task queue is empty, the thread will block until a task (Runnable Task) is put into it. in the task queue.

This is why the task queue of the thread pool needs to be a blocking queue.

We said before that the thread pool in Java is a producer-consumer model. The user of the thread——"produces a runnable task Runnable task, and puts it in the task queue) for consumption by the threads in the thread pool (execution of the task) )

4. When the number of threads in the thread pool is equal to corePoolsize, and the task queue workQueue is full.

At this time, if a new Runnable Task comes, the thread pool will create a new thread to process the task.

Until the number of threads reaches the maximumPoolSize——"It will not continue to create new threads to process tasks.

These new threads will not be destroyed after executing the current task. Instead, execute the RunnableTask in the task queue (the thread is busy at this time, not idle) when the tasks in the task queue are executed (the number of threads in the thread pool should be greater than corePoolSize at this time, then there will be A judgment logic——“ Judge whether the thread needs to be destroyed. When the thread is blocked (entering the idle state) for a time longer than keepAliveTime because the task queue is empty, the thread will be destroyed——” until the number of threads is equal to corePoolSize

5. If the number of threads in the thread pool reaches maximumSize, and the task queue is also full at this time.

In this case, if there is a new task coming, it will be processed directly by the rejected processor. The default handler logic is to throw a RejectedExecutionException

The whole flow chart is as follows:

a little supplement

When using the thread pool, you should also pay attention to exception handling. For example, when submitting a task through the execute() method of the ThreadPoolExecutor object, if a runtime exception occurs during the execution of the task, the thread executing the task will be terminated; however, the most deadly What's more, although the task is abnormal, you can't get any notification , which will make you mistakenly think that the task is executed normally. Although the thread pool provides many methods for exception handling, the safest and simplest solution is to catch all exceptions and handle them on demand

Ideas and ideas about the creation of thread pools:

Reference: Thread Creation Overhead and Thread Pool - Know About

Guess you like

Origin blog.csdn.net/weixin_61061381/article/details/129147938