很多时候我们需要在退出的时候能够保证内存中的数据得到处理,比如在写文件的时候程序要退出,此时如果直接退出的话会造成部分数据还没有持久化到文件中,从而造成数据丢失。为了解决这个问题,我们需要在程序退出的时候能够捕获到该消息,然后做一些数据清理操作,目前有两种方案:
一种是采用SignalHandler类来进行相应的处理
另外一种是在JVM里注册一个“关闭钩子”(shutdown hook)
1、关于第一种方法,我们来看一个例子,代码如下:
/**
* 信号注册及捕捉处理类
* @author xiaoxiao
*/
@Component
public class SignalHelper implements SignalHandler {
private Log log = LogFactory.getLog(getClass());
@PostConstruct
public void registSignal() {
//注意这里并不是什么消息都能捕获,像kill -9或者断电等退出操作就捕获不了,所以尽量少用kill -9
Signal.handle(new Signal("TERM"), this); //kill -15
Signal.handle(new Signal("INT"), this); //CRTL+C
log.info("注册信号成功");
}
@Override
public void handle(Signal signal) { //捕捉信号,使程序优雅地退出
log.info("捕捉到信号:" + signal.getName());
new CleanerTask().run();
System.exit(0); //捕捉到终止信号若不调用exit方法,则程序不会退出
}
}
其中CleanerTask就是一个线程类,它里面会进行相应的数据清理操作,这个根据具体业务做相应操作,在程序启动的时候务必要执行registSignal函数,如果有退出信号,例如kill -15,此时就会执行handle函数。
2、关于第二种方法,注册"关闭钩子",代码如下:
AbstractApplicationContext ctx;
// 加载spring配置文件
ctx = new FileSystemXmlApplicationContext("mybean.xml");
//为了在非web环境下让spring容器优雅的关闭
ctx.registerShutdownHook();
ctx.start();
这里之所以用钩子,主要是用来在程序退出的时候将IOC容器资源释放掉,像web程序中自动会进行IOC容器资源释放,所以不需要我们做任何释放操作,但是在非web程序中,就不会自动释放spring中的IOC容器资源,所以需要我们手动进行释放资源。
当然我们也可以实现自己的关闭钩子(关闭钩子可以有多个),示例代码如下:
public class JVMHook {
public static void start(){
System.out.println("The JVM is started");
Runtime.getRuntime().addShutdownHook(new Thread(){
public void run(){
try{
//do something
System.out.println("The JVM Hook is execute");
}catch (Exception e) {
e.printStackTrace();
}
}
});
}
public static void main(String[] args) {
start();
System.out.println("The Application is doing something");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果为:
The JVM is started
The Application is doing something
The JVM Hook is execute
参考文章: