Java 多线程 线程池的生命周期及运行状态
- 2022-10-18 江西
本文字数:4535 字
阅读完需:约 1 分钟
一、说明
线程池的生命周期
线程池的状态
runState
和工作线程数量workerCount
共同保存在AtomicInteger
类型的控制变量ctl
中ctl 高三位保存运行状态(2^3^=8>5),低 29 位保存工作线程的数量(2^29^-1)
// 初始运行状态为RUNNING,线程数为0
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// COUNT_BITS: 29
private static final int COUNT_BITS = Integer.SIZE - 3;
// CAPACITY: 十进制: 536870911 二进制: 00011111111111111111111111111111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
// RUNNING: 十进制:-536870912 二进制:11100000000000000000000000000000
private static final int RUNNING = -1 << COUNT_BITS;
// SHUTDOWN: 十进制:0 二进制:0
private static final int SHUTDOWN = 0 << COUNT_BITS;
// STOP: 十进制:536870912 二进制:00100000000000000000000000000000
private static final int STOP = 1 << COUNT_BITS;
// TIDYING: 十进制:1073741824 二进制:01000000000000000000000000000000
private static final int TIDYING = 2 << COUNT_BITS;
// TERMINATED: 十进制:1610612736 二进制:01100000000000000000000000000000
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl 打包和解包ctl
// 获取线程池当前状态,CAPACITY取反,高三位都是1,低29位都是0,和ctl进行与运算,获得runState变量
private static int runStateOf(int c) { return c & ~CAPACITY; }
// CAPACITY高三位都是0,低29位都是0,和ctl进行与运算获得workerCount变量
private static int workerCountOf(int c) { return c & CAPACITY; }
// 初始化ctl变量,runState和workerCount进行或运算后共同存储在一个变量中
private static int ctlOf(int rs, int wc) { return rs | wc; }
RUNNING
接收新的任务,并且可执行队列里的任务SHUTDOWN
停止接收新任务,但可执行队列里的任务STOP
可执行队列里的任务,不执行队列里的任务,中断正在执行的任务TIDYING
所有任务都已终止,线程数为 0,当线程池变为 TIDYING 状态时,会执行钩子函数 terminated(),钩子方法是指使用一个抽象类实现接口,一个抽象类实现这个接口,需要的方法设置为 abstract,其它设置为空方法TERMINATED
终止状态,表示线程池已关闭,已经执行完 terminated()钩子方法
判断当前线程池运行状态
// 判断线程池当前运行状态是否小于给定值
private static boolean runStateLessThan(int c, int s) {
return c < s;
}
// 判断线程池当前运行状态是否大于等于给定值
private static boolean runStateAtLeast(int c, int s) {
return c >= s;
}
// 判断线程池是否处于RUNNING状态
private static boolean isRunning(int c) {
return c < SHUTDOWN;
}
// 判断线程池是否处于SHUTDOWN状态
public boolean isShutdown() {
return ! isRunning(ctl.get());
}
// 判断线程池是否处于TERMINATING状态
public boolean isTerminating() {
int c = ctl.get();
return ! isRunning(c) && runStateLessThan(c, TERMINATED);
}
// 判断线程池是否处于TERMINATED状态
public boolean isTerminated() {
return runStateAtLeast(ctl.get(), TERMINATED);
}
运行状态转换关系
二、理解
RUNNING
接收新的任务,并且可执行队列里的任务
// 初始运行状态为RUNNING,线程数为0
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
SHUTDOWN
shutdown()
方法将线程池状态转换为SHUTDOWN
,停止接收新任务,但可执行队列里的任务
public void shutdown() {
// 上锁确保只有一个线程执行此操作
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查是否有权限关闭线程池以和中断线程
checkShutdownAccess();
// 将线程池状态设置为SHUTDOWN
advanceRunState(SHUTDOWN);
// 中断所有空闲线程
interruptIdleWorkers();
// 用于取消延时任务
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// 将线程池置为TERMINATED状态
tryTerminate();
}
private void checkShutdownAccess() {
// 检查是否有安全管理器,确保调用者有权关闭线程
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(shutdownPerm);
// 上锁确保只有一个线程执行此操作
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 对工作的线程做安全权限检查
for (Worker w : workers)
security.checkAccess(w.thread);
} finally {
mainLock.unlock();
}
}
}
private void advanceRunState(int targetState) {
// assert targetState == SHUTDOWN || targetState == STOP;
// CAS 自旋操作,死循环将线程池运行状态设置为目标值
for (;;) {
// 获取AtomicInteger 类型的控制变量 ctl
int c = ctl.get();
// 判断当前线程池的状态值是否大于目标值
if (runStateAtLeast(c, targetState) ||\
// 将线程池运行状态设置为目标值,成功的话会返回true,失败则返回false
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
// 中断所有空闲线程
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
// 如果参数为false,中断所有空闲线程,如果为true,则只中断一个空闲线程
private void interruptIdleWorkers(boolean onlyOne) {
// 上锁确保只有一个线程执行此操作
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查所有的工作线程是否被中断
for (Worker w : workers) {
Thread t = w.thread;
// 如果没有被中断,并且工作线程获得了锁,则执行中断方法,并释放线程锁
// 成功则返回true,表示处于无锁状态的worker(state为0)为空闲线程
// 失败则返回false,表示处于锁定状态的worker(state为1)为工作线程,
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
// 如果为true,表示只中断一个空闲线程,退出循环,适用于用tryTerminate()方法
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
STOP
shutdownNow()
方法将线程池状态转换为STOP
,同时中断所有线程,停止接收新任务,不执行队列里的任务,中断正在执行的任务
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
// 上锁确保只有一个线程执行此操作
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查是否有权限关闭线程池以和中断线程
checkShutdownAccess();
// 将线程池运行状态置为STOP
advanceRunState(STOP);
// 中断所有线程
interruptWorkers();
// 将未执行的任务移入列表中
tasks = drainQueue();
} finally {
mainLock.unlock();
}
// 将线程池置为TERMINATED状态
tryTerminate();
return tasks;
}
TIDYING
所有任务都已终止,线程数为 0,线程池变为TIDYING
状态,会执行钩子函数terminated()
,钩子方法是指使用一个抽象类实现接口,一个抽象类实现这个接口,需要的方法设置为abstract
,其它设置为空方法
// 每个工作线程的终结都会调用tryTerminate()方法
final void tryTerminate() {
for (;;) {
int c = ctl.get();
// 判断线程池的状态,下面几种情况则直接返回
// 1. 线程池处于RUNNING状态
// 2. 线程池处于TIDYING或者TERMINATED状态,意味着已经走到了下面的步骤,线程池即将终结
// 3. 线程池处于SHUTDOWN状态且任务队列不为空
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
// 到这里说明当前线程池除了线程数不为0,其他条件都已经满足关闭要求
// 如果工作线程数量不为0,则尝试中断工作线程集合中的一个空闲线程
if (workerCountOf(c) != 0) {
interruptIdleWorkers(ONLY_ONE);
return;
}
// 到这里说明线程池满足所有关闭条件的要求,接下来将线程池运行状态置为TERMINATED
// 上锁确保只有一个线程执行此操作
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// CAS设置线程池运行状态为TIDYING
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
// 执行terminated()钩子方法,用于终止线程池
terminated();
} finally {
// 将线程池运行状态置为TERMINATED,且线程数为0
ctl.set(ctlOf(TERMINATED, 0));
// 唤醒阻塞在termination条件的所有线程
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
termination
实现 Condition
接口,用于实现线程阻塞和唤醒
private final ReentrantLock mainLock = new ReentrantLock();
private final Condition termination = mainLock.newCondition();
TERMINATED
执行完terminated()
钩子方法,线程池已终止,变为TERMINATED
状态
三、实现
使用ThreadPoolExecutor
自定义线程池
版权声明: 本文为 InfoQ 作者【Yeats_Liao】的原创文章。
原文链接:【http://xie.infoq.cn/article/507b4875f9a56f169ec29490a】。文章转载请联系作者。
Yeats_Liao
Hello,World! 2022-10-02 加入
这里更多的是记录个人学习,如果有侵权内容请联系我! 个人邮箱是:yeats_liao@foxmail.com
评论