前言
本文接上篇线程池的源码分析,上篇主要通过实例且围绕源码分析了 线程池的 线程复用、线程回收以及执行原理。由于上篇文章的篇幅太长了,所以才有了本姊妹篇。
深入浅出线程池的源码
一、shutDown 和 shutDownNow 的区别
1. shutDown 方法会把线程池的状态置为 SHUT_DOWN,shutDownNow 将线程池的状态设置为 STOP
2. shutDown 不会中断正在运行执行任务的线程,而 shutDownNow 则会调用 interrupt() 方法来中断阻塞的任务。阻塞的任务 表示的是任务中调用了 sleep、wait等方法使执行任务的线程阻塞。下面会具体分析
3. 调用shutDown后阻塞队列中的任务还是会被执行,而shutDownNow 则不会执行阻塞队列中的任务了。
二、shutDown 和 shutDownNow的源码分析
1. shutDown 源码:
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查调用者是否有权限来 shut dwon 线程,这个不用管
checkShutdownAccess();
// 设置线程池的状态为 SHUTDOWN,且设置 ctl 的值
advanceRunState(SHUTDOWN);
// 尝试中断正在执行任务的线程,里面的逻辑是调用AQS的 tryLock
// 由于正在执行任务的线程开始的时候都调用了 lock,所以tryLock返回false
// 即不会调用中断方法 interrupt
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// 主要目的是把线程池调用过 shutDown 或者 shutDownNow方法后,
// 将线程池的状态转变为 TERMINATED
tryTerminate();
}
下面逐一分析下 advanceRunState、interruptIdleWorkers、tryTerminate;
advanceRunState:
private void advanceRunState(int targetState) {
// assert targetState == SHUTDOWN || targetState == STOP;
for (; ; ) {
int c = ctl.get();
int tct = workerCountOf(c); // 获取线程池中创建的线程个数
// 位运算 或 操作
int ctc = ctlOf(targetState, tct);
// 判断线程池的状态是否为SHUT_DOWN 或 STOP,
// 如果不是则将线程池的状态设置为SHUT_DOWN 或 STOP
if (runStateAtLeast(c, targetState)
|| ctl.compareAndSet(c, ctc))
break;
}
}
interruptIdleWorkers:
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
interruptIdleWorkers(boolean onlyOne) 的代码如下:
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// workers 里面存放的是正在执行任务的线程集合,
// 开始执行任务的时候会把work加进去
for (Worker w : workers) {
Thread t = w.thread;
// 如果任务没有被中断且通过tryLock获取锁成功,则执行线程的中断
// 前面也分析过,任务开始执行的时候都会调用lock方法,结束才release,
// 所以如果任务没有执行完,tryLock获取锁会失败
// 注意,AQS的lock会阻塞当前线程,而tryLock则不会阻塞,会立刻返回结果;
// 这也是为什么 shunDown无法中断正在执行的任务。
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
tryTerminate :
final void tryTerminate() {
for (; ; ) {
int c = ctl.get();
// 条件1:线程池是运行状态
// 条件2:线程池的状态至少是 TIDYING 且阻塞队列不为空
// 满足就直接return了
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && !workQueue.isEmpty())) {
return;
}
// 如果当前线程池中至少有一个线程,那么尝试中断1个正在执行任务的线程
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 设置线程池的状态为 TIDYING
boolean flag = ctl.compareAndSet(c, ctlOf(TIDYING, 0));
// 如果上面设置状态为TIDYING成功,则把线程池设置为最后状态TERMINATED
if (flag) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
从上面的代码分析可知:
- shutDown 方法 将 线程池的状态设置为 SHUT_DOWN;
- shutDown 方法不会中断正在执行的任务;
- 得不出能够继续执行阻塞队列中任务的结论。这个结论可以从 getTask中得出结论。这个可以从上篇的源码分析中得出。
1. shutDownNow 源码:
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();
}
// 见上面分析
tryTerminate();
return tasks;
}
这里的 advanceRunState 方法传入的参数是 STOP,主要分析下 interruptWorkers
,如下:
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
然后调用的是 Worker 类中的 interruptIfStarted
方法,如下:
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null
&& !t.isInterrupted()) { // **注1**
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
来分析下代码中的 if 条件 if (getState() >= 0 && (t = thread) != null && !t.isInterrupted())
:
-
getState()调用的是AQS中的方法,因为Worker类继承了AQS;
-
getState() > 0,表示的是有线程获取到锁了;
因为开始执行任务的时候,会调用 w.lock()方法, 这个方法会把state的值加1,所以getState()>0 成立。 -
thread 表示的是正在执行任务的线程,肯定不为null,条件成立,t.isInterrupted() 表示此线程是否被中断,基本上为false, 除非在在任务中自己调用 Thread.currentThread.interrupt()
综上所分析 ,这个if条件是成立的,所以会执行 t.interrupt()
; 上面在提到 shutDown 和 shutDownNow在是否中断正在执行任务的线程时说到:
shutDownNow 则会调用 interrupt() 方法来中断
阻塞的任务
这里说明下,interrupt
方法只会中断阻塞的线程,什么意思呢?
如果你的任务 Runnable 任务中调用了阻塞的方法,如 sleep、wait等,如下:
Runnable runnable2 = () -> {
try {
Thread.sleep(3_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
在 调用 shutDownNow
方法后,就会抛出中断异常,结束正在执行的任务,即中断了正在执行任务的线程。
如果 你的任务中不会调用阻塞线程的方法,如下:
Runnable runnable1 = () -> {
long i = 0;
while (true){
Log.e("test thread pool", "r1 start i = "+(i++));
}
那么,就算你调用了 shutDownNow ,该任务也会一直执行下去。
所以分析shutDownNow
方法得出的结论是:
- shutDownNow把线程池的状态设置为
STOP
- shutDownNow 则会调用 interrupt() 方法来中断
阻塞的线程
- 同样得不出 shutDownNow 不会执行阻塞队列中的任务,这个可以从 getTask中得出:
我截取部分 getTask 的代码分析就够了:、
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (; ; ) {
int c = ctl.get();
int rs = runStateOf(c); // 获取线程池的状态
// 调用 shutDownNow 后,线程池的状态是 STOP ,所以rs >= SHUTDOWN成立
// 所以rs >= STOP成立,因为 STOP > SHUTDOWN (=0)。即返回null,
// 返回null就不会再去阻塞队列中获取任务啦
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
Log.e("test thread pool", "--getTask c = " + ctl.get());
return null;
}
。。。。。。
}
}
好嘞,线程池系列完结啦。欢迎交流指正。