Java程序的安全退出

一、原理

Signal实例表示一个信号量实例
将Signal实例注册到SignalHandler中实现对该信号量的监听
当监听到信号量,就会回调handle方法

特别注意:安全退出是通过监听信号量回调方法运行,并不是让程序退出。而其他方法是程序主动关闭,会停止其他线程运行shudownhook线程进行扫尾关闭

二、它和关闭钩子的区别

1、关闭钩子:会终止main线程让整个程序停下来后执行shutdownHook线程进行扫尾工作、
2、监听信号量:系统发送信号量以后,会先被Java程序中的监听程序线程捕获信号量,不影响main线程的继续运行,可以在其中调用System.exit(0)来关闭程序,这个时候就会正常关闭程序,运行shutdownhook线程

三、例子

public class ShutdownTest {
    public static void main(String[] args) {
        System.out.println("Shutdown Test");

        Signal sg = new Signal("TERM"); // kill -15 pid
        // 监听信号量
        //将sg这个信号量实例注册到一个SignalHandler实例中
        Signal.handle(sg, new SignalHandler() {
        //收到指定信号量就回调handle()方法
            @Override
            public void handle(Signal signal) {
                System.out.println("signal handle: " + signal.getName());
                System.exit(0);
            }
        });
        // 注册关闭钩子
        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run() {
                // 在关闭钩子中执行收尾工作
                // 注意事项:
                // 1.在这里执行的动作不能耗时太久
                // 2.不能在这里再执行注册,移除关闭钩子的操作
                // 3 不能在这里调用System.exit()
                System.out.println("do shutdown hook");
            }
        });

        mockWork();

        System.out.println("Done.");
        System.exit(0);
    }

    // 模拟进程正在运行
    private static void mockWork() {
        //mockRuntimeException();
        //mockOOM();
        try {
            Thread.sleep(120 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } 
    }

    // 模拟在应用中抛出RuntimeException时会调用注册钩子
    private static void mockRuntimeException() {
        throw new RuntimeException("This is a mock runtime ex");
    }

    // 模拟应用运行出现OOM时会调用注册钩子
    // -xms10m -xmx10m
    private static void mockOOM() {
        List list = new ArrayList();
        for(int i = 0; i < 1000000; i++) {
            list.add(new Object());
        }
    }
}