【问题标题】:Catch multiple Ctrl+C keypresses in Java to do graceful and forced shutdowns在 Java 中捕获多个 Ctrl+C 按键以执行优雅和强制关闭
【发布时间】:2022-02-08 13:33:35
【问题描述】:

在我的 Java 控制台应用程序中,我捕获了一个 Ctrl+C 按键,并使用 Runtime.getRuntime().addShutdownHook() 添加了一个执行正常关闭的线程

在这个线程中,我在一些内部队列中处理所有未完成的工作并优雅地退出我的应用程序,这通常是我想要的。

但是,有时应用程序的内部队列可能需要一段时间(几分钟)才能处理,如果是这样,我希望可以选择再次按 Ctrl+C 以强制退出应用程序。

我见过其他应用程序以这种方式工作,但我不知道如何在 Java 中捕捉第二个 Ctrl+C?

【问题讨论】:

    标签: java hook interrupt shutdown


    【解决方案1】:

    警告,这不是你应该做的。 The usual reasons 避免使用太阳包。在实际代码中实际使用它之前,请三思而后行。至少应该通过反射来访问信号处理,并在失败时回退到注册通常的关闭挂钩。为了简洁起见,我把它省略了。

    所以,所有的希望都放弃吧,你们进入这里:

    import java.util.concurrent.atomic.AtomicBoolean;
    import sun.misc.Signal;
    import sun.misc.SignalHandler;
    
    public class Sigint {
        private static void prepareShutdownHandling() {
            // This would be the normal shutdown hook
            final Thread shutdown = new Thread() {
                @Override
                public void run() {
                    // Simulate slow shutdown
                    for (int i = 0; i < 10; i++) {
                        System.out.println("Normal shutdown running");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("Normal shutdown finished");
                    System.exit(0);
                }
            };
    
            Signal.handle(new Signal("INT"), new SignalHandler() {
                private AtomicBoolean inShutdown = new AtomicBoolean();
                public void handle(Signal sig) {
                    if (inShutdown.compareAndSet(false, true)) {
                        // Normal shutdown
                        shutdown.start();
                    } else {
                        System.err.println("Emergency shutdown");
                        System.exit(1);
                    }
                }
            });
        }
    
        public static void main(String args[]) {
            prepareShutdownHandling();
            while (true) {
                System.out.println("Main program running");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    【讨论】:

    • 感谢您提供优雅的解决方案!所以本质上归结为不可能使用Java以可移植/跨平台的方式进行所需的信号处理? (不使用 sun.* 库)或者如果平台之间的信号处理差异很大,也许真的可以使用任何语言?
    • @Nils 我很难称之为优雅:-D。 AFAIK 没有可移植的方式,并且 supported 不可移植的方式(信号链)在通常的信号处理之后运行处理程序,所以我认为它不起作用(我没有尝试过)。但也许有人想出了更可靠的破解方法。
    • 看起来没有 sun.* 的方法是:可以使用 -Xrs 参数抑制默认的 SIGINT 处理程序。然后可以在本机代码中完成信号处理。该解决方案的可移植性也不是很好 - 无论是命令行开关还是本机代码。
    【解决方案2】:

    在 SIGINT 和处理程序中使用 Signal.handle,恢复原始默认的 SIG_DFL 处理程序。因此,如果它被第二次命中,它将使用 SIG_DFL 处理程序(由操作系统提供)终止程序。

    private void registerSigIntHandler() {
        Signal.handle(new Signal("INT"), new SignalHandler() {
            public void handle(Signal sig) {
                log.info("SIGINT received");
                Signal.handle(new Signal("INT"), SignalHandler.SIG_DFL);
                shutdown();
                SignalHandler.SIG_DFL.handle(new Signal("INT"));
            }
        });
    }
    

    这还会调用 SIG_DFL 处理程序以在您的 shutdown 方法完成后终止进程。

    https://stackoverflow.com/a/71028985/854342

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-03
      • 1970-01-01
      • 1970-01-01
      • 2018-01-10
      • 2019-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多