# 《手写线程池》线程池核心技术-第05节:线程池优雅关闭核心流程解析

作者:冰河
星球:http://m6z.cn/6aeFbs (opens new window)
博客:https://binghe.gitcode.host (opens new window)
文章汇总:https://binghe.gitcode.host/md/all/all.html (opens new window)
源码获取地址:https://t.zsxq.com/0dhvFs5oR (opens new window)

沉淀,成长,突破,帮助他人,成就自我。

  • 本章难度:★★★☆☆
  • 本章重点:简单介绍线程池优雅关闭的核心流程,重点掌握线程池中的shutdown()方法触发优雅关闭的流程,对比解析shutdown()方法和shutdownNow()方法的执行流程,以及线程池中awaitTermination()方法的执行流程。从全局视角掌握线程池的核心技术原理,学会融汇贯通,将线程池的编程思想灵活应用到自身实际项目中,提升实际项目的并发处理能力,以及自身的并发编程内功功底。

大家好,我是冰河~~

正是因为线程池有一套完善的优雅关闭机制和执行流程,才能保证线程池在关闭时,不再接收新任务,但仍能够继续执行已经提交到线程池的任务,直到所有提交到线程池的任务执行完毕后,线程池才会关闭并退出。线程池的优雅关闭机制和执行流程的设计思想,可以复用到任何需要优雅关闭的项目和业务场景。

# 一、前言

上一节中,主要从源码角度分析了Worker线程的核心执行流程,重点介绍了线程池中Worker线程的执行流程和涉及的方法调用,主要包含:runWorker()方法、getTask()方法、beforeExecute()方法、afterExecute()方法、processWorkerExit()方法、tryTerminate()方法和terminated()方法等,掌握这些核心方法的执行流程,基本就掌握了线程池中Worker线程的执行流程。接下来,继续深入解析和探讨线程池优雅关闭的核心流程。

# 二、本节诉求

简单介绍线程池优雅关闭的核心流程,重点掌握线程池中的shutdown()方法触发优雅关闭的流程,对比解析shutdown()方法和shutdownNow()方法的执行流程,以及线程池中awaitTermination()方法的执行流程。从全局视角掌握线程池的核心技术原理,学会融汇贯通,将线程池的编程思想灵活应用到自身实际项目中,提升实际项目的并发处理能力,以及自身的并发编程内功功底。

# 三、核心流程解析

线程池的优雅关闭流程能够保证线程池在关闭时,不再接收新任务,但仍能够继续执行已经提交到线程池的任务,,直到所有提交到线程池的任务执行完毕后,线程池才会关闭并退出。接下来,就对比分析线程池中的shutdown()方法、shutdownNow()方法和awaitTermination()方法,来深入解析线程池的优雅关闭流程。

# 3.1 shutdown()方法解析

当使用线程池的时候,调用了shutdown()方法后,线程池就不会再接受外界的执行任务了。但是在调用shutdown()方法之前放入任务队列中的任务还是要执行。此方法是非阻塞方法,调用后会立即返回,并不会等待任务队列中的任务全部执行完毕后再返回。shutdown()方法的源代码如下所示。

public void shutdown() {
	//获取线程池的全局锁
	final ReentrantLock mainLock = this.mainLock;
	mainLock.lock();
	try {
		//检查是否有关闭线程池的权限
		checkShutdownAccess();
		//将当前线程池的状态设置为SHUTDOWN
		advanceRunState(SHUTDOWN);
		//中断Worker线程
		interruptIdleWorkers();
		//为ScheduledThreadPoolExecutor调用钩子函数
		onShutdown(); // hook for 
	} finally {
		//释放线程池的全局锁
		mainLock.unlock();
	}
	//尝试将状态变为TERMINATED
	tryTerminate();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

总体来说,shutdown()方法的代码比较简单,首先检查了是否有权限来关闭线程池,如果有权限,则再次检测是否有中断工作线程的权限,如果没有权限,则会抛出SecurityException异常,源码如下所示。

//检查是否有关闭线程池的权限
checkShutdownAccess();
//将当前线程池的状态设置为SHUTDOWN
advanceRunState(SHUTDOWN);
//中断Worker线程
interruptIdleWorkers();
1
2
3
4
5
6

其中,checkShutdownAccess()方法的源码如下所示。

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();
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

checkShutdownAccess()方法的源码比较简单,就是检测是否具有关闭线程池的权限,期间使用了线程池的mainLock全局锁。

接下来,再分析下advanceRunState(int)方法,源码如下所示。

private void advanceRunState(int targetState) {
	for (;;) {
		int c = ctl.get();
		if (runStateAtLeast(c, targetState) ||
			ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
			break;
	}
}
1
2
3
4
5
6
7
8

advanceRunState(int)方法的整体逻辑就是:判断当前线程池的状态是否为指定的状态,在shutdown()方法中传递的状态是SHUTDOWN,如果是SHUTDOWN,则直接返回;如果不是SHUTDOWN,则将当前线程池的状态设置为SHUTDOWN。

接下来,再分析下showdown()方法调用的interruptIdleWorkers()方法,源码如下所示。

private void interruptIdleWorkers() {
	interruptIdleWorkers(false);
}
1
2
3

可以看到,interruptIdleWorkers()方法调用的是interruptIdleWorkers(boolean)方法,继续分析interruptIdleWorkers(boolean)方法的源代码,如下所示。

private void interruptIdleWorkers(boolean onlyOne) {
	final ReentrantLock mainLock = this.mainLock;
	mainLock.lock();
	try {
		for (Worker w : workers) {
			Thread t = w.thread;
			if (!t.isInterrupted() && w.tryLock()) {
				try {
					t.interrupt();
				} catch (SecurityException ignore) {
				} finally {
					w.unlock();
				}
			}
			if (onlyOne)
				break;
		}
	} finally {
		mainLock.unlock();
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

interruptIdleWorkers(boolean)方法的总体逻辑为:获取线程池的全局锁,循环所有的工作线程,检测线程是否被中断,如果没有被中断,并且Worker线程获得了锁,则执行线程的中断方法,并释放线程获取到的锁。此时如果onlyOne参数为true,则退出循环。否则,循环所有的工作线程,执行相同的操作。最终,释放线程池的全局锁。

至此,shutdown()方法的执行逻辑分析完毕。

# 3.2 shutdownNow()方法解析

如果调用了线程池的shutdownNow()方法,则线程池不会再接受新的执行任务,也会将任务队列中存在的任务丢弃,正在执行的Worker线程也会被立即中断,同时,方法会立刻返回,并且shutdownNow()方法存在一个返回值,也就是当前任务队列中被丢弃的任务列表。shutdownNow()方法的源码如下所示。

public List<Runnable> shutdownNow() {
	List<Runnable> tasks;
	final ReentrantLock mainLock = this.mainLock;
	mainLock.lock();
	try {
		//检查是否有关闭权限
		checkShutdownAccess();
		//设置线程池的状态为STOP
		advanceRunState(STOP);
		//中断所有的Worker线程
		interruptWorkers();
		//将任务队列中的任务移动到tasks集合中
		tasks = drainQueue();
	} finally {
		mainLock.unlock();
	}
	/尝试将状态变为TERMINATED
	tryTerminate();
	//返回tasks集合
	return tasks;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

shutdownNow()方法源码的总体逻辑与shutdown()方法基本相同,只是shutdownNow()方法将线程池的状态设置为STOP,中断所有的Worker线程,并且将任务队列中的所有任务移动到tasks集合中并返回。

另外,通过shutdownNow()方法的源码也可以看出,shutdownNow()方法中断所有的线程时,调用了interruptWorkers()方法,接下来,分析下interruptWorkers()方法的源代码,如下所示。

# 查看完整文章

加入冰河技术 (opens new window)知识星球,解锁完整技术文章、小册、视频与完整代码