捕获Ctrl+C信号时抛出异常“作业管理器已关闭”

7

我想要添加信号支持(尤其是Ctrl+C),我的工具是用Java编写的,当捕获到Ctrl+C时,我想执行清理操作。我的主文件是Application,以下是代码片段:

if (ArgDefinitions.getInstance().hasOption(ArgNames.EXECUTE)) {
    performShutdownHooks(); 
    preformRun();
}

应用程序解析用户的选项并运行适当的方法。因此,当用户使用“执行”选项并点击“Ctrl+C”时,我希望程序能够停止并清理区域。 为了处理信号,我添加了“performShutdownHooks”方法,代码如下:
private void performShutdownHooks() {
    Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
            performCleanup();
        }
    });
}

这个工具能够做我想要的事情-如果我运行它并在运行时杀死它,它会清理(它在后台运行一个特殊的命令)。 问题出现在我没有停止它的时候。我会得到以下异常:

Exception in thread "Thread-2" java.lang.IllegalStateException: Job manager has been shut down.
        at org.eclipse.core.internal.jobs.JobManager.schedule(JobManager.java:1104)
        at org.eclipse.core.internal.jobs.InternalJob.schedule(InternalJob.java:427)
        at org.eclipse.core.runtime.jobs.Job.schedule(Job.java:436)
        at glichautil.CommandExecutor.runCommandInBackground(CommandExecutor.java:134)
        at glicha.core.Application.performCleanup(Application.java:869)
        at glicha.core.Application.access$0(Application.java:838)
        at glicha.core.Application$1.run(Application.java:985)
        

我认为即使我没有尝试杀死进程,performShutdownHooks仍会在最后运行。 也许是其他因素导致它被杀死,但不应该这样,因为如果我注释掉方法调用performShutdownHooks(),它就能正常工作(没有抛出任何异常)。 这让我相信,由于某种原因,即使我没有按下Ctrl+CperformShutdownHooks方法仍然在运行。 是否需要在performShutdownHooks方法中添加一些内容以解决此问题?或者我错过了与addShutdownHook有关的内容? 如果这是问题的正确解释,那么我认为,如果我能以某种方式取消它在最后的运行,那么它将解决问题。 还可能会发生一些其他线程在代码中被杀死并且由于某些原因执行了该方法。
编辑:我认为我已经理解了为什么会出现这种情况。在进入我的ShutdownHooks方法之前,它先打印:
Job found still running after platform shutdown.
Jobs should be canceled by the plugin that scheduled them during shutdown: glicha.testmanager.HandleParallelJobs.

我猜这个问题与ShutdownHooks有关。问题在于,我不知道如何解决。我从文档中了解到:
Java虚拟机响应两种事件而关闭: 1.程序正常退出,即最后一个非守护线程退出或调用退出方法(等效于System.exit); 2.响应用户中断,例如键入Ctrl+C,或系统范围内的事件,例如用户注销或系统关闭。
因此我猜,当插件关闭时,它会调用ShutdownHooks(如果我错了,请纠正)。问题在于,我不知道如何区分这两种可能性。我只想捕捉信号并执行清理工作,但如果VM之一关闭,我不想这样做。有什么想法吗?

我也遇到了类似的问题。你解决了吗? - kickstart
嗨@kickstart,请查看我的编辑。 - vesii
你的 performCleanup 方法实际上是做什么的?引起关闭的 'glicha' 是什么东西,是你的代码还是第三方框架? - second
@second 你好,感谢回复。Glicha 是该工具的名称。performCleanup 停止所有正在运行的作业。Schedule 是一个第三方框架(可能甚至是 Java 内置的)。 - vesii
1
不连贯的代码片段并不是特别有帮助。请更新您的问题,将其转化为 MCVE。我需要一些可以编译和运行的东西来重现您的问题。您广泛的散文很好,但在那方面并没有帮助我。您不能指望任何人仅凭文本信息就能重新创建您的问题。具体而言,我完全不理解为什么您只想在按下 Ctrl-C 而不是应用程序以其他方式退出时执行清理。为什么不采用防御性编程并在关闭挂钩中处理异常呢? - kriegaex
2个回答

0

您可以覆盖SecurityManagercheckExit(int status)方法 - 如果在任何地方显式调用了System.exit(status),则此方法有效 - 但是,当应用程序“正常”退出(没有活动线程)或错误终止VM时,它不会设置状态。

System.setSecurityManager(new ExitMonitorSecurityManager());
Runtime.getRuntime().addShutdownHook(new Thread(new MyShutdownHook()));

private static class ExitMonitorSecurityManager extends SecurityManager {

        @Override
        public void checkPermission(Permission perm) {
            //Something;
        }

        @Override
        public void checkPermission(Permission perm, Object context) {
            //Something
        }

        @Override
        public void checkExit(int status) {
            System.out.println("Setting exit value via security manager...");
            MyShutdownHook.EXIT_STATUS = status;
        }
    }

    private static class MyShutdownHook implements Runnable {

        public static Integer EXIT_STATUS;

        public void run() {

            System.out.println("In MyShutdownHook - exit status is " + EXIT_STATUS);
        }
    }

0

关闭挂钩的常见问题之一是没有保证关闭事物的顺序。我假设在您的情况下,Schedule框架可能已经有了自己的关闭挂钩,它已经执行了清理(停止任务)。

与关闭挂钩相关的javadoc解释了一些内容,即您不能依赖于其他资源的存在,因为它们可能已经被关闭。

现在,当您尝试在Shutdown hook中执行此操作时,根据关闭挂钩的执行顺序,您可能会收到上述错误。


这些并不是你所要求的,但我猜你可能根本没有问题。我宁愿讨论以下事项:

  • 你为什么想要首先拥有一个关闭挂钩?
  • 为什么(感觉)需要自己停止正在运行的作业?

您可能希望将清理时运行的代码添加到您的问题中。根据我所了解的信息,您可能希望关闭已经关闭的某些内容。


如果你仍然想要进行一些信号拦截,实际上是有方法的,但我不建议这样做。根据这个链接https://www.javaspecialists.eu/archive/Issue043.html,你可以使用sun.misc.*包中的Signal类。

sun.misc包只包含非官方API,可能随时被删除而没有进一步的通知。目前似乎没有官方API支持你的用例(至少在我所知道的范围内)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接