Abstract: combines the source code of the ThreadPoolExecutor class to deeply analyze the overall process of thread pool execution tasks.
This article is shared from Huawei Cloud Community " [High Concurrency] In-depth analysis of the core process of thread pool execution tasks through the source code of the ThreadPoolExecutor class ", author: Glacier.
There is a worker thread collection in the ThreadPoolExecutor class. Users can add tasks to be executed to the thread pool. The worker threads in the workers collection can execute tasks directly or execute tasks after obtaining tasks from the task queue. The ThreadPoolExecutor class provides the entire process method of the entire thread pool from creation to execution of tasks, and then to death. This article will combine the source code of the ThreadPoolExecutor class to deeply analyze the overall process of thread pool execution tasks.
In the ThreadPoolExecutor class, the logic of the thread pool is mainly reflected in the execute (Runnable) method, addWorker (Runnable, boolean) method, addWorkerFailed (Worker) method and rejection strategy. Next, we will analyze these core methods in depth.
execute(Runnable) method
The function of execute(Runnable) method is to submit tasks of type Runnable to the thread pool. Let's first look at the source code of the execute(Runnable) method, as shown below.
public void execute(Runnable command) {
//如果提交的任务为空,则抛出空指针异常
if (command == null)
throw new NullPointerException();
//获取线程池的状态和线程池中线程的数量
int c = ctl.get();
//线程池中的线程数量小于corePoolSize的值
if (workerCountOf(c) < corePoolSize) {
//重新开启线程执行任务
if (addWorker(command, true))
return;
c = ctl.get();
}
//如果线程池处于RUNNING状态,则将任务添加到阻塞队列中
if (isRunning(c) && workQueue.offer(command)) {
//再次获取线程池的状态和线程池中线程的数量,用于二次检查
int recheck = ctl.get();
//如果线程池没有未处于RUNNING状态,从队列中删除任务
if (! isRunning(recheck) && remove(command))
//执行拒绝策略
reject(command);
//如果线程池为空,则向线程池中添加一个线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//任务队列已满,则新增worker线程,如果新增线程失败,则执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
The execution flow of the entire task can be simplified as shown in the figure below.
Next, we disassemble the execute(Runnable) method and analyze the execution logic of the execute(Runnable) method in detail.
(1) Whether the number of threads in the thread pool is less than the number of corePoolSize core threads, if it is less than the number of corePoolSize core threads, add a core thread to the worker thread collection to perform tasks. The code is shown below.
//线程池中的线程数量小于corePoolSize的值
if (workerCountOf(c) < corePoolSize) {
//重新开启线程执行任务
if (addWorker(command, true))
return;
c = ctl.get();
}
(2) If the number of threads in the thread pool is greater than the number of core threads in corePoolSize, it is judged whether the current thread pool is in the RUNNING state, and if it is in the RUNNING state, the task is added to the task queue to be executed. Note: When adding tasks to the task queue, you need to determine whether the thread pool is in the RUNNING state. Only when the thread pool is in the RUNNING state can you add new tasks to the task queue. Otherwise, the rejection policy will be implemented. The code is shown below.
if (isRunning(c) && workQueue.offer(command))
(3) The task is successfully added to the task queue. Since other threads may modify the state of the thread pool, the thread pool needs to be checked twice. If the current thread pool status is no longer in the RUNNING state, you need to add The task is removed from the task queue and the subsequent rejection strategy is executed. If the current thread pool is still in the RUNNING state, it is judged whether the thread pool is empty. If there is no thread in the thread pool, a new thread is added to the thread pool, as shown below.
//再次获取线程池的状态和线程池中线程的数量,用于二次检查
int recheck = ctl.get();
//如果线程池没有未处于RUNNING状态,从队列中删除任务
if (! isRunning(recheck) && remove(command))
//执行拒绝策略
reject(command);
//如果线程池为空,则向线程池中添加一个线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
(4) If adding a task to the task queue fails in step (3), try to start a new thread to execute the task. At this point, if the number of threads in the thread pool is already greater than the maximum number of threads in the thread pool maximumPoolSize, no more threads can be started. At this point, it means that the task queue in the thread pool is full, and the threads in the thread pool are full, and the rejection policy needs to be executed. The code is as follows.
//任务队列已满,则新增worker线程,如果新增线程失败,则执行拒绝策略
else if (!addWorker(command, false))
reject(command);
Here, we disassemble the execute(Runnable) method and understand the execution flow of tasks in the thread pool in combination with the flowchart. It can be said that the logic of the execute(Runnable) method is basically the execution logic of the general thread pool. Understanding the execute(Runnable) method can basically understand the execution logic of the thread pool.
Note: The logic of the ScheduledThreadPoolExecutor class and the ForkJoinPool class execution thread pool will be explained in detail later in the [High Concurrency Topics] series of articles. After understanding the execution logic of these classes, you will basically have a comprehensive grasp of the execution process of the thread pool.
When analyzing the source code of the execute(Runnable) method, we found that the addWorker(Runnable, boolean) method was called in multiple places in the execute(Runnable) method. Next, we will analyze the logic of the addWorker(Runnable, boolean) method together.
addWorker(Runnable, boolean) method
In general, the addWorker(Runnable, boolean) method can be divided into three parts. The first part is to use CAS to safely add worker threads to the thread pool; the second part is to create a new worker thread; the third part is to pass the task safely The concurrency mode is added to the workers, and the worker threads are started to perform tasks.
Next, we look at the source code of the addWorker(Runnable, boolean) method, as shown below.
private boolean addWorker(Runnable firstTask, boolean core) {
//标记重试的标识
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 检查队列是否在某些特定的条件下为空
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
//下面循环的主要作用为通过CAS方式增加线程的个数
for (;;) {
//获取线程池中的线程数量
int wc = workerCountOf(c);
//如果线程池中的线程数量超出限制,直接返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//通过CAS方式向线程池新增线程数量
if (compareAndIncrementWorkerCount(c))
//通过CAS方式保证只有一个线程执行成功,跳出最外层循环
break retry;
//重新获取ctl的值
c = ctl.get();
//如果CAS操作失败了,则需要在内循环中重新尝试通过CAS新增线程数量
if (runStateOf(c) != rs)
continue retry;
}
}
//跳出最外层for循环,说明通过CAS新增线程数量成功
//此时创建新的工作线程
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//将执行的任务封装成worker
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
//独占锁,保证操作workers时的同步
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//此处需要重新检查线程池状态
//原因是在获得锁之前可能其他的线程改变了线程池的状态
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
throw new IllegalThreadStateException();
//向worker中添加新任务
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
//将是否添加了新任务的标识设置为true
workerAdded = true;
}
} finally {
//释放独占锁
mainLock.unlock();
}
//添加新任成功,则启动线程执行任务
if (workerAdded) {
t.start();
//将任务是否已经启动的标识设置为true
workerStarted = true;
}
}
} finally {
//如果任务未启动或启动失败,则调用addWorkerFailed(Worker)方法
if (! workerStarted)
addWorkerFailed(w);
}
//返回是否启动任务的标识
return workerStarted;
}
At first glance, the addWorker(Runnable, boolean) method is quite long. Here, we still disassemble the addWorker(Runnable, boolean) method.
(1) Check whether the task queue is empty under certain conditions, the code is as follows.
// 检查队列是否在某些特定的条件下为空
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
(2) After passing the verification of step (1), enter the inner for loop. In the inner for loop, CAS is used to increase the number of threads in the thread pool. If the CAS operation is successful, the double for loop is directly exited. If the CAS operation fails, check whether the state of the current thread pool has changed. If the state of the thread pool has changed, use the continue keyword to re-check the task queue through the outer for loop, and verify by executing the inner for loop again CAS operation. If the state of the thread pool has not changed, and the last CAS operation failed at this time, continue to try the CAS operation. The code is shown below.
for (;;) {
//获取线程池中的线程数量
int wc = workerCountOf(c);
//如果线程池中的线程数量超出限制,直接返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//通过CAS方式向线程池新增线程数量
if (compareAndIncrementWorkerCount(c))
//通过CAS方式保证只有一个线程执行成功,跳出最外层循环
break retry;
//重新获取ctl的值
c = ctl.get();
//如果CAS操作失败了,则需要在内循环中重新尝试通过CAS新增线程数量
if (runStateOf(c) != rs)
continue retry;
}
(3) After the CAS operation is successful, it means that the worker thread has been successfully added to the thread pool. At this time, there is no thread to perform the task. Use the global exclusive lock mainLock to safely add the new worker object to the workers.
The overall logic is: create a new Worker object and obtain the thread of execution in the Worker object. If the thread is not empty, obtain the exclusive lock. After the lock is successfully obtained, check the status of the thread thread again. This is to avoid obtaining the exclusive lock before Other threads have modified the state of the thread pool or closed the thread pool. If the thread pool is closed, the lock needs to be released. Otherwise, the newly added thread is added to the work set, the lock is released and the thread is started to perform the task. Set the flag of whether to start the thread to true. Finally, it is judged whether the thread is started, and if it is not started, the addWorkerFailed(Worker) method is called. Finally, return the ID of whether the thread has started or not.
//跳出最外层for循环,说明通过CAS新增线程数量成功
//此时创建新的工作线程
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//将执行的任务封装成worker
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
//独占锁,保证操作workers时的同步
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//此处需要重新检查线程池状态
//原因是在获得锁之前可能其他的线程改变了线程池的状态
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
throw new IllegalThreadStateException();
//向worker中添加新任务
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
//将是否添加了新任务的标识设置为true
workerAdded = true;
}
} finally {
//释放独占锁
mainLock.unlock();
}
//添加新任成功,则启动线程执行任务
if (workerAdded) {
t.start();
//将任务是否已经启动的标识设置为true
workerStarted = true;
}
}
} finally {
//如果任务未启动或启动失败,则调用addWorkerFailed(Worker)方法
if (! workerStarted)
addWorkerFailed(w);
}
//返回是否启动任务的标识
return workerStarted;
addWorkerFailed(Worker) method
In the addWorker(Runnable, boolean) method, if adding a worker thread fails or the worker thread fails to start, the addWorkerFailed(Worker) method will be called. Let’s take a look at the implementation of the addWorkerFailed(Worker) method, as shown below.
private void addWorkerFailed(Worker w) {
//获取独占锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//如果Worker任务不为空
if (w != null)
//将任务从workers集合中移除
workers.remove(w);
//通过CAS将任务数量减1
decrementWorkerCount();
tryTerminate();
} finally {
//释放锁
mainLock.unlock();
}
}
The logic of the addWorkerFailed(Worker) method is relatively simple. Obtain an exclusive lock, remove tasks from workers, and reduce the number of tasks by 1 through CAS, and finally release the lock.
Rejection strategy
When we analyze the execute (Runnable) method, the thread pool will call the reject (Runnable) method at an appropriate time to execute the corresponding rejection strategy. Let's look at the implementation of the reject (Runnable) method, as shown below.
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
Through the code, we found that the rejectedExecution method of the handler is called, and what the hell is the handler, we continue to follow up the code, as shown below.
private volatile RejectedExecutionHandler handler;
Let's take a look at what type RejectedExecutionHandler is, as shown below.
package java.util.concurrent;
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
It can be found that RejectedExecutionHandler is an interface that defines a rejectedExecution(Runnable, ThreadPoolExecutor) method. Since RejectedExecutionHandler is an interface, let's see which classes implement the RejectedExecutionHandler interface.
Seeing this, we find that the implementation class of the RejectedExecutionHandler interface is exactly the implementation class of the four rejection strategies provided by the thread pool by default.
As for which type of rejection strategy will be executed in the reject (Runnable) method, it is determined according to the parameters passed when the thread pool is created. If no rejection policy is passed, the rejection policy of the AbortPolicy class will be executed by default. Otherwise, the rejection policy of the passed class will be executed.
When creating a thread pool, in addition to being able to pass the rejection policy provided by the JDK by default, you can also pass a custom rejection policy. If you want to use a custom rejection strategy, you only need to implement the RejectedExecutionHandler interface and override the rejectedExecution(Runnable, ThreadPoolExecutor) method. For example, the following code.
public class CustomPolicy implements RejectedExecutionHandler {
public CustomPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
System.out.println("使用调用者所在的线程来执行任务")
r.run();
}
}
}
Use the following method to create a thread pool.
new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
Executors.defaultThreadFactory(),
new CustomPolicy());
At this point, the overall core logic analysis of the thread pool execution task is over.
Click to follow to learn about Huawei Cloud's fresh technology for the first time~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。