优雅的关闭JAVA程序
背景
在线上Java程序中经常遇到进程程挂掉,一些状态没有正确的保存下来,这时候就需要在JVM关掉的时候执行一些清理现场的代码。Java中得ShutdownHook提供了比较好的方案。
JDK在1.3之后提供了Java Runtime.addShutdownHook(Thread hook)方法,可以注册一个JVM关闭的钩子,这个钩子可以在以下几种场景被调用:
- 程序正常退出
- 使用System.exit()
- 终端使用Ctrl+C触发的中断
- 系统关闭
- 使用Kill pid命令干掉进程
注:在使用kill -9 pid是不会JVM注册的钩子不会被调用。
什么是Shutdown Hook
Shutdown hook是一个initialized but unstarted thread。当JVM开始执行shutdown sequence时,会并发运行所有registered Shutdown Hook。这时,在Shutdown Hook这个线程里定义的操作便会开始执行。
需要注意的是,在Shutdown Hook里执行的操作应当是不太耗时的。因为在用户注销或者操作系统关机导致的JVM shutdown的例子中,系统只会预留有限的时间给未完成的工作,超时之后还是会强制关闭。
如何使用Shutdown Hook
调用java.lang.Runtime这个类的addShutdownHook(Thread hook)方法即可注册一个Shutdown Hook,然后在Thread中定义需要在system exit时进行的操作。如下:
Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("Do something in Shutdown Hook")));
测试代码
public class ShutDownHook {
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread()
{
@Override
public void run()
{
System.out.println("Interrupting threads");
Set<Thread> runningThreads = Thread.getAllStackTraces().keySet();
for (Thread runningThread : runningThreads) {
// 判断线程是否是我们要测试的线程
if (runningThread.getName().equals("InteruptThread")) {
// 如果线程没有被中断,我们进行中断他
if (!runningThread.isInterrupted()) {
System.out.println("InteruptThread is not interrupted, we are going to interupt it");
runningThread.interrupt();
}
}
}
System.out.println("Shutdown hook ran!");
}
});
// 创建并启动子线程类
InteruptThread thread = new InteruptThread();
thread.start();
}
/**
* 用来测试要中断的线程类
*/
static class InteruptThread extends Thread {
public InteruptThread() {
this.setName("InteruptThread");
}
@Override
public void run() {
while (true) {
try {
while (true) {
// 打印个时间
System.out.println(System.currentTimeMillis());
// 等待10秒
TimeUnit.SECONDS.sleep(10);
}
} catch (InterruptedException e) {
System.out.println("oh yeah, InteruptThread is interrupted");
break;
} catch (Exception e) {
System.out.println("unknown exception ocurred");
break;
}
}
}
}
}