研究ThreadPoolExecutor源码时,发现两个方法:shutdown()和shutdownNow(),从名字上看都是关闭,只是后者多了一个Now,那么它们两个有什么区别呢?
首先先来看代码:
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);//通过自旋锁不断的修改线程池状态,直到修改成功为止
interruptIdleWorkers();
onShutdown(); // 空实现,子类可以覆盖
} finally {
mainLock.unlock();
}
tryTerminate();
}
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);//通过自旋锁不断的修改线程池状态,直到修改成功为止
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
先来说共同点:
- 都是public的;
- 调用这两个方法之后,调用execute()方法添加新任务都会被拒绝;
- 都会对线程发出中断;
- 执行线程中断和修改状态前,都会加锁;
- 如果阻塞队列中没有任务而且没有运行的线程,两个方法都会将线程池状态修改为TERMINATED,这个操作是在tryTerminate()中完成的;
- 调用这两个方法都会对正在运行的线程发出中断,但是不会强行销毁线程,因此尽管执行了这两个方法,线程池可能还会运行一段时间。
下面来看一下两者的不同点:
- 修改线程池的状态不同,shutdown()将状态修改为SHUTDOWN,shutdownNow()将状态修改为STOP,修改状态时,调用的方法都是相同的:
//入参是目标状态
private void advanceRunState(int targetState) {
for (;;) {
int c = ctl.get();
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
- shutdown()对空闲线程发起中断,而shutdownNow()对所有的线程发起中断;
- shutdownNow()调用drainQueue()将阻塞队列清空,并且将阻塞队列中的任务返回给调用方,下面是drainQueue()的代码;
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
//新建List对象,用于存储阻塞队列中的任务
ArrayList<Runnable> taskList = new ArrayList<Runnable>();
q.drainTo(taskList);//清空阻塞队列,将任务放到新建的List中
if (!q.isEmpty()) {
for (Runnable r : q.toArray(new Runnable[0])) {
if (q.remove(r))
taskList.add(r);
}
}
return taskList;
}
- 两个方法的最后都调用了tryTerminate(),因为shutdown()不会清空阻塞队列,所以两个方法执行tryTerminate()还有些区别,如果阻塞队列中还有任务,或者还有正在运行的线程,都会造成shutdown()返回,而shutdownNow()只关心是否还有运行的线程,如果有,选择一个线程发起中断便返回了。
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) {
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
}
}