微信搜索“捉虫大师”,点赞、关注是对我最大的鼓励,本文已收录于 https://github.com/lkxiaolou/lkxiaolou
ShutdownHook介绍
在java程序中,很容易在进程结束时添加一个钩子,即ShutdownHook
。通常在程序启动时加入以下代码即可
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
System.out.println("I'm shutdown hook...");
}
});
有了ShutdownHook我们可以
- 在进程结束时做一些善后工作,例如释放占用的资源,保存程序状态等
- 为优雅(平滑)发布提供手段,在程序关闭前摘除流量
不少java中间件或框架都使用了ShutdownHook的能力,如dubbo、spring等。
spring中在application context被load时会注册一个ShutdownHook。
这个ShutdownHook会在进程退出前执行销毁bean,发出ContextClosedEvent等动作。
而dubbo在spring框架下正是监听了ContextClosedEvent,调用dubboBootstrap.stop()
来实现清理现场和dubbo的优雅发布,spring的事件机制默认是同步的,所以能在publish事件时等待所有监听者执行完毕。
ShutdownHook原理
ShutdownHook的数据结构与执行顺序
- 当我们添加一个ShutdownHook时,会调用
ApplicationShutdownHooks.add(hook)
,往ApplicationShutdownHooks
类下的静态变量private static IdentityHashMap<Thread, Thread> hooks
添加一个hook,hook本身是一个thread对象 ApplicationShutdownHooks
类初始化时会把hooks
添加到Shutdown
的hooks
中去,而Shutdown
的hooks
是系统级的ShutdownHook,并且系统级的ShutdownHook由一个数组构成,只能添加10个- 系统级的ShutdownHook调用了thread类的
run
方法,所以系统级的ShutdownHook是同步有序执行的
private static void runHooks() {
for (int i=0; i < MAX_SYSTEM_HOOKS; i++) {
try {
Runnable hook;
synchronized (lock) {
// acquire the lock to make sure the hook registered during
// shutdown is visible here.
currentRunningHook = i;
hook = hooks[i]