通过Thread Pool Executor类解析线程池执行任务的核心流程

摘要:ThreadPoolExecutor是Java线程池中最核心的类之一,它能够保证线程池按照正常的业务逻辑执行任务,并通过原子方式更新线程池每个阶段的状态 。
本文分享自华为云社区《【高并发】通过Thread Pool Executor类的源码深度解析线程池执行任务的核心流程》,作者:冰 河 。
今天,我们通过Thread Pool Executor类的源码深度解析线程池执行任务的核心流程,小伙伴们最好是打开IDEA,按照步骤 , 调试下Thread Pool Executor类的源码,这样会理解的更加深刻,好了,开始今天的主题 。
核心逻辑概述Thread PoolExecutor是Java线程池中最核心的类之一,它能够保证线程池按照正常的业务逻辑执行任务,并通过原子方式更新线程池每个阶段的状态 。
ThreadPoolExecutor类中存在一个workers工作线程集合,用户可以向线程池中添加需要执行的任务,workers集合中的工作线程可以直接执行任务,或者从任务队列中获取任务后执行 。ThreadPoolExecutor类中提供了整个线程池从创建到执行任务,再到消亡的整个流程方法 。本文,就结合ThreadPoolExecutor类的源码深度分析线程池执行任务的整体流程 。
在ThreadPoolExecutor类中,线程池的逻辑主要体现在execute(Runnable)方法,addWorker(Runnable, boolean)方法,addWorkerFailed(Worker)方法和拒绝策略上 , 接下来,我们就深入分析这几个核心方法 。
execute(Runnable)方法execute(Runnable)方法的作用是提交Runnable类型的任务到线程池中 。我们先看下execute(Runnable)方法的源码,如下所示 。
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);}整个任务的执行流程,我们可以简化成下图所示 。
通过Thread Pool Executor类解析线程池执行任务的核心流程

文章插图
接下来 , 我们拆解execute(Runnable)方法,具体分析execute(Runnable)方法的执行逻辑 。
(1)线程池中的线程数是否小于corePoolSize核心线程数,如果小于corePoolSize核心线程数,则向workers工作线程集合中添加一个核心线程执行任务 。代码如下所示 。
//线程池中的线程数量小于corePoolSize的值if (workerCountOf(c) < corePoolSize) {//重新开启线程执行任务if (addWorker(command, true))return;c = ctl.get();}(2)如果线程池中的线程数量大于corePoolSize核心线程数,则判断当前线程池是否处于RUNNING状态,如果处于RUNNING状态,则添加任务到待执行的任务队列中 。注意:这里向任务队列添加任务时,需要判断线程池是否处于RUNNING状态,只有线程池处于RUNNING状态时,才能向任务队列添加新任务 。否则,会执行拒绝策略 。代码如下所示 。
【通过Thread Pool Executor类解析线程池执行任务的核心流程】if (isRunning(c) && workQueue.offer(command)) (3)向任务队列中添加任务成功,由于其他线程可能会修改线程池的状态,所以这里需要对线程池进行二次检查 , 如果当前线程池的状态不再是RUNNING状态 , 则需要将添加的任务从任务队列中移除,执行后续的拒绝策略 。如果当前线程池仍然处于RUNNING状态 , 则判断线程池是否为空,如果线程池中不存在任何线程,则新建一个线程添加到线程池中,如下所示 。
//再次获取线程池的状态和线程池中线程的数量,用于二次检查int recheck = ctl.get();//如果线程池没有未处于RUNNING状态,从队列中删除任务if (! isRunning(recheck) && remove(command))//执行拒绝策略reject(command);//如果线程池为空,则向线程池中添加一个线程else if (workerCountOf(recheck) == 0)addWorker(null, false);(4)如果在步骤(3)中向任务队列中添加任务失败 , 则尝试开启新的线程执行任务 。此时,如果线程池中的线程数量已经大于线程池中的最大线程数maximumPoolSize,则不能再启动新线程 。此时,表示线程池中的任务队列已满,并且线程池中的线程已满,需要执行拒绝策略,代码如下所示 。

推荐阅读