JVM Shutdown Hook

共 6806字,需浏览 14分钟

 ·

2023-06-14 13:34




须弥零一JVM Shutdown Hook

今天偶然看到 java.lang.Runtime 类的一个方法 public void addShutdownHook(Thread hook) 。


它的 javadoc 是这么写的:



Registers a new virtual-machine shutdown hook.
The Java virtual machine shuts down in response to two kinds of events:



  • • The program exits normally, when the last non-daemon thread exits or when the exit (equivalently, System.exit) method is invoked, or




  • • The virtual machine is terminated in response to a user interrupt, such as typing ^C, or a system-wide event, such as user logoff or system shutdown.




意思就是说可以给 JVM 注册一个钩子,这个钩子将在虚拟机关闭的执行。当然这个 关闭 是有条件的。


写个例子





public
 
class
 
ShutdownHookTest
 
{


    
public
 
static
 
void
 
main

(
String
[]
 args
)

 
{

        
System
.
out
.
println
(
"Main thread start"
);

        
Thread
 
hook
 
=
 
new
 
Thread
(
new
 
MyShutdownHook
());

        
Runtime
.
getRuntime
().
addShutdownHook
(
hook
);

        
System
.
out
.
println
(
"Main thread end"
);

    
}


    
static
 
class
 
MyShutdownHook
 
implements
 
Runnable
 
{

        
@Override

        
public
 
void
 
run
()
 
{

            
System
.
out
.
println
(
"-- my shutdown hook start --"
);

            
try
 
{

                
System
.
out
.
println
(
"-- do hook task --"
);

                
Thread
.
sleep
(
5
 
*
 
1000L
);

                
System
.
out
.
println
(
"-- hook task finished --"
);

            
}
 
catch
 
(
InterruptedException
 e
)
 
{

                e
.
printStackTrace
();

            
}

            
System
.
out
.
println
(
"-- my shutdown hook end --"
);

        
}

    
}


}


使用 javac 编译后运行,得到的输出结果是:





Main
 thread start

Main
 thread 
end

--
 
my
 shutdown hook start 
--

--
 
do
 hook task 
--

--
 hook task finished 
--

--
 
my
 shutdown hook 
end
 
--


结果符合文章刚开始引文的第一个情况,当程序的正常退出时会执行注册的钩子。也就是说,在程序主线程(实际上是所有的 demon线程)结束后,会启动执行钩子线程。


程序非执行完成推出的例子


稍微改一下上面的代码:





public
 
class
 
ShutdownHookTest
 
{


    
public
 
static
 
void
 
main

(
String
[]
 args
)

 
throws
 
Exception
{

        
System
.
out
.
println
(
"Main thread start"
);

        
Thread
 
hook
 
=
 
new
 
Thread
(
new
 
MyShutdownHook
());

        
Runtime
.
getRuntime
().
addShutdownHook
(
hook
);

        
// --- 改了这里 ---

        
Thread
.
sleep
(
120
 
*
 
1000L
);

        
// --------------

        
System
.
out
.
println
(
"Main thread end"
);

    
}


    
static
 
class
 
MyShutdownHook
 
implements
 
Runnable
 
{

        
@Override

        
public
 
void
 
run
()
 
{

            
System
.
out
.
println
(
"-- my shutdown hook start --"
);

            
try
 
{

                
System
.
out
.
println
(
"-- do hook task --"
);

                
Thread
.
sleep
(
5
 
*
 
1000L
);

                
System
.
out
.
println
(
"-- hook task finished --"
);

            
}
 
catch
 
(
InterruptedException
 e
)
 
{

                e
.
printStackTrace
();

            
}

            
System
.
out
.
println
(
"-- my shutdown hook end --"
);

        
}

    
}


}


同样使用 javac 编译后运行。不同的是,在程序启动后按 ctrl + c 停止程序,将得到下面的输出:





Main
 thread start

--
 
my
 shutdown hook start 
--

--
 
do
 hook task 
--

--
 hook task finished 
--

--
 
my
 shutdown hook 
end
 
--


这个结果也符合文章开始引文的第二个情况。当虚拟机用为用户输入 ^C 时,虚拟机会调用已注册的钩子。


另外因为也提到了当用户注销系统关闭时也会调用已注册的钩子,这里就不做验证了。


钩子不能执行的情况


同样是上面的例子。程序在启动后,打开任务管理器(Windows),找到对应的进程并结束。这时控制台的输出为:





Main
 thread start


从输出可以看到,钩子并没有执行。


这就说明,在虚拟机中止 (注意:这里的中止不同于退出或停止,是指异常的break) 的情况下,钩子不会被调用执行。


javadoc 也给出了这种情况的说明:



在极少数虚拟机被外部中止的情况下,例如:



  • • 在 Unix 上使用 SIGKILL 信号




  • • 在 Windows 上使用 TerminateProcess 调用




  • • 执行本地方法出错



也就是说,虚拟机在没有干净地关闭的情况下停止运行,虚拟机则不能保证是否正确的运行关机钩子。



移除钩子


移除钩子使用方法 public boolean removeShutdownHook(Thread hook) 即可。


使用场景


看到这个特性,第一个想到的场景就是。程序在关闭时可以记录一个日志或发送一个通知。


当然,这个基于这个特性,可以定制出来很多使用场景。


但是,鉴于这个钩子的执行时机,就有很多需要注意的地方:



  • • 关机钩子(shutdown hook)必须是一个已初始化但未启动的线程




  • • 如果注册了多个钩子,则不能保证这些钩子的执行顺序,他们是同时开始的




  • • 当虚拟机关机序列开始,则无法在注册或取消钩子




  • • 对于钩子的线程的编写,应该是线程安全的,并尽可能地避免出现死锁




  • • 钩子的执行时间不应过长,也不应该添加任何用户交互功能



    • • 这时因为当用户注销或者关机时,底层操作系统可能只允许有限的固定时间来关闭和退出虚拟机




最后


文章就写到这里。


这篇文章没啥深入的探究,只是突然发现了一个之前未曾注意到的功能,简单的做一下记录和测试。


大家如果要在生产环境中使用,要是场景复杂还是慎重些,上一章节的那些注意事项都需要考虑考虑。



---- END ----


欢迎关注我的公众号“须弥零一”,原创技术文章第一时间推送。





浏览 60
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报