在开发中,遇到一个问题。我们每次重启一个应用的时候,总会导致一些正在执行的任务因为机器突然死掉,如果不对这种正在执行的任务记录,处理的话,这些任务就消失了。在重启那个时刻jvm里所发生的所有事情我们都已无所知。这会导致什么样的后果,很难知道。发生的后果也难以管理。所以必须对这种情况进行监控,处理。
后来知道了java的addShutdownHook钩子函数,试了试,很管用。下面是这个函数的官方中文API:
Java 虚拟机会为了响应以下两类事件而关闭
:
-
程序正常退出
,这发生在最后的非守护线程退出时,或者在调用
exit
(等同于 System.exit
)方法时。或者,
-
为响应用户中断而终止
虚拟机,如键入 ^C
;或发生系统事件,比如用户注销或系统关闭。
关闭钩子
只是一个已初始化但尚未启动的线程。虚拟机开始启用其关闭序列时,它会以某种未指定的顺序启动所有已注册的关闭钩子,并让它们同时运行。运行完所有的钩子
后,如果已启用退出终结,那么虚拟机接着会运行所有未调用的终结方法。最后,虚拟机会暂停。注意,关闭序列期间会继续运行守护线程,如果通过调用 exit
方法来发起关闭序列,那么也会继续运行非守护线程。
一旦开始了关闭序列,则只能通过调用 halt
方法来停止这个序列,此方法可强行终止虚拟机。
一旦开始了关闭序列,则不可能注册新的关闭钩子或取消注册先前已注册的钩子。尝试执行这些操作会导致抛出 IllegalStateException
。
关闭钩子可在虚拟机生命周期中的特定时间运行,因此应保护性地对其进行编码。特别是应将关闭钩子编写为线程安全的,并尽可能地避免死锁。关闭钩子还应该不
盲目地依靠某些服务,这些服务可能已注册了自己的关闭钩子,所以其本身可能正处于关闭进程中。例如,试图使用其他基于线程的服务(如 AWT
事件指派线程)可能导致死锁。
关闭钩子应该快速地完成其工作。当程序调用 exit
时,虚拟机应该迅速地关闭并退出。由于用户注销或系统关闭而终止虚拟机时,底层的操作系统可能只允许在固定的时间内关闭并退出。因此在关闭钩子中尝试进行任何用户交互或执行长时间的计算都是不明智的。
与其他所有线程一样,通过调用线程 ThreadGroup
对象的 uncaughtException
方法,可在关闭钩子中处理未捕获的异常。此方法的默认实现是将该异常的堆栈跟踪打印至 System#err
并终止线程;它不会导致虚拟机退出或暂停。
仅在很少的情况下,虚拟机可能会中止
,也就是没有完全关闭就停止运行。虚拟机被外部终止时会出现这种现象,比如在 Unix 上使用 SIGKILL
信号或者在 Microsoft Windows 上调用 TerminateProcess
。如果由于内部数据结构损坏或试图访问不存在的内存而导致本机方法执行错误,那么可能也会中止虚拟机。如果虚拟机中止,则无法保证是否将运行关闭钩子。
自己写了个测试的东西,大概讲了怎么使用:
主类:
public class TestHook {
public static void main(String[] args) {
TestDO td = new TestDO();
TestHook th = new TestHook();
th.doSomething();
System.exit(0);
}
public void doSomething() {
System.out.println("doSomething");
}
}
注册钩子的类:
public class TestDO {
TestDO() {
System.out.println("registe");
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
System.out.println("log");
}
});
}
}
很显然,结果是:
registe
doSomething
log
这只是一个很简单的例子,在实际开发中,我是直接写了一个注册类,构造函数为执行addShutdownHook(),把它配置为一个spring的bean。直接交由Spring去管理。值得注意的是,对某些突然直接杀死jvm进程的操作,钩子函数也是不靠谱的,比如Linux命令: kill -9
使用钩子函数有个千万要注意
的地方,如果你在钩子内写了死循环,会发生永远不会执行结束的情况,那系统会永远无法按照正常情况关闭。只能杀java进程了!如:
public class Test {
public Test() {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
System.out.println("log");
while (true) {
System.out.println("杯具大了");
}
}
});
}
public static void main(String[] args) {
Test t = new Test();
System.exit(0);
}
}
分享到:
相关推荐
JDK在1.3之后提供了Java Runtime.addShutdownHook(Thread hook)方法,可以注册一个JVM关闭的钩子,这个钩子可以在以下几种场景被调用: 1)程序正常退出 2)使用System.exit() 3)终端使用Ctrl+C触发的...
Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { try { server.stop(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }); } Or ...
使用addShutDownHook()附加到客户端进程的线程处理用户中止。 这个线程在Client.java被定义为一个内部类。 处理用户离开/加入服务器或 MUD,使用MUDServerMainline的shutdownhook MUDServerMainline和服务器端方法...
SpringBoot Rest与以下库中的JVM Futures结合 ... Runtime.getRuntime().addShutdownHook(new Thread(SpringBootFuturesSimulation::shutdown)); new SpringApplicationBuilder(SpringWebFuturesAppl
1 增加线程池consumer优雅退出机制Runtime.getRuntime().addShutdownHook 2 修改部分log输出方式,将原来的 log.info("exceptin:" + e) 修复为 log.info("exception: ", e) 20161227 更新 1 bug fix: 将...