
线程池状态
定义
在简单介绍完线程池的基本概念后,这篇文章我们来分析一下,线程池它具体是如何工作的。首先我们看一下线程池的状态。线程池的设计是这样子的,它将线程池的状态和目前工作线程的线程数用一个变量来进行了存储,就是ctl变量
1 | private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); |
首先这是一个原子类,int类型,32位,前3位是用来标识线程池的状态的,后29位是用来表示线程池当中工作的线程数的。

通过使用原子类,这样就能保证线程池的状态和工作线程数的原子性了。那么,我们当前线程池的状态常量是如何标识的呢,以及当我们想要查询线程池的工作线程数,是怎样运算的呢。
1 | private static final int COUNT_BITS = Integer.SIZE - 3; // 29位用来存工作线程数 |
- 线程池有5种状态,按小到大的顺序如下:RUNNING<SHUTDOWN<STOP<TIDYING<TERMINATED
- 使用原子类来存储两个变量,保证了共享变量的原子性,以及它按照位操作提升了很大的性能
状态解析
RUNNING:in operation
运行,当线程池初始化的时候,线程池就处于RUNNING状态,处于该状态时,线程池可以接受任务,并且处理缓冲的任务。
SHUTDOWN:turn off
关闭,当调用shutdown()方法时,线程池从RUNING转化为SHUTDOWN状态,该状态的线程池不接受任务,只处理缓冲的任务
STOP:stop
停止,当调用shutdownNow()方法时,会将(RUNING/SHUTDOWN状态)转换成该状态,处于该状态的线程池不接受任务,不处理缓冲任务,并且中断全部正在处理的任务interrupt running tasks
TIDYING:清理,当任务中断了,并且线程工作数为0,就会进入清理状态,清理现场。
When the thread pool is in the SHUTDOWN state, the blocking queue is empty and the tasks executed in the thread pool are also empty, it will be SHUTDOWN - > tidying. When the task executed in the thread pool is empty in the STOP state, it will be STOP - > tidying.
TERMINATED:终止,结束,当terminated()方法执行完后,就会进入该状态
When the thread pool terminates completely, it becomes terminated. When the thread pool is in tidying state, after executing terminated(), it will be tidying - > terminated.
状态转化
1 | * RUNNING -> SHUTDOWN |
通过介绍我们知道,线程池初始化时处于RUNNING状态
,当线程池调用shutdown()
方法时,线程池就关闭了,只处理当前已经进来的任务。当任务处理完之后,线程池会调用terminated()
方法,执行完后线程池进入TERMINATED状态
。当然还有另一种方式,不管线程池有没有调用shutdown()
方法,如果调用了shutdownNow()
,会中断正在执行的线程,当队列和线程池线程为空时,线程池就会进入stop状态
。当池中线程为空时,terminated()
方法执行完后进入TERMINATED状态
。
构造方法
1 | public ThreadPoolExecutor(int corePoolSize, |
提交执行task的过程
解释
当提交一个新任务到线程池时,线程池的处理流程如下:
- 线程池判断核心线程数是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果是,则进入步骤2。
- 线程池判断工作队列是否已满。如果没有满,将新提交的任务存储在工作队列中。如果满了,则进入步骤3。
- 线程池判断线程池中线程是否都处于工作状态(已满)。如果没有,则创建一个新的工作线程。如果满了,则交给饱和策略来处理这个任务。
execute方法
1 | /** |
execute方法本身来说并不难,我们来这样子分析。
第一步,传入来的command不能为空
第二步,取到当前ctl,拿到当前工作线程数与核心数比较,如果小于,直接创建worker执行任务,否则进入下一步。
注意,addWorkerf方法的第二个参数表示是否核心线程。方法本身会去判断当前线程池状态和数量的,如果addWork失败,方法会返回false,表示添加任务失败,在重新获取一下ctl,用于下一步
第三步,走到这一步说明,不是核心数满了,就是线程池shutdown了。看一下当前线程池状态,如果是运行状态,就尝试往队列中放任务,如果放成功了返回true,放任务前后都得去拿一下当前的ctl,再去判断一下线程池是否shutdown了,如果shutdown了,就把任务从队列中remove掉。如果没有shutdown,会去判断当前工作线程数。
如果为0,我就启动一个线程,并且这里传入的firstTask还是null,这是为什么呢?
第四步,这一步相当重要,走到这一步,说明线程池不是运行状态了,或者任务进入队列失败了,为了消费任务,会尝试创建(非核心)worker执行任务,如果创建失败,则要采取饱和策略,执行reject操作。
addWorker源码解析
干了两件事情
使用CAS算法将workerCount加一
将任务装入到worker中,并且将work添加到工作线程集合workers中,开始执行任务
worker是一个任务单元,继承了AQS类,本身是把锁,而且构造方法是RUNNALE任务,因此可以使用start方法执行线程中的run方法
如果线程启动失败,将这个worker从当前线程池works集合中删除,并且将workerCount减一
1 | private boolean addWorker(Runnable firstTask, boolean core) { |
worker任务单元
worker是一个任务单元,他里面包含了一个线程,和一个任务,然后还记录了我这个worker干过多少个任务等等。
1 | private final class Worker |
runworker核心线程执行逻辑
1 | final void runWorker(Worker w) { |
这里相当于用到了一种装饰者模式,将一个task包装成一个带有锁特性的执行者。这个执行者不仅可以消费当前的任务,getTask
还能从阻塞队列中获取任务消费,当队列为空的时候,线程会被阻塞住。当有任务过来的时候,又可以执行了。当任务执行完之后,processWorkerExit
方法是线程worker的出口。他会处理wc和workers集合,并且尝试将当前线程中断。
参考:
Deep understanding of thread pool ThreadPool Executor
Do you know the underlying implementation of thread pools?Thread Pool Module Summary