捕获Ctrl + C信号将引发异常“作业管理器已关闭”

时间:2019-06-04 23:05:12

标签: java signals shutdown-hook

我正在尝试添加对信号的支持(尤其是Ctrl + C)。 我的工具是用Java编写的,当我发现Ctrl+C时,我想执行清理。 我的主文件是应用程序,代码如下:

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

应用程序解析用户的选项并运行适当的方法。因此,当用户使用execute选项并单击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()注释掉,它可以正常工作(不会引发任何异常)。 这使我相信,由于某种原因,即使我没有按performShutdownHooks,方法Ctrl+C仍在运行。 为了解决它,我需要添加到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虚拟机因以下两种事件而关闭:

     
      
  • 当最后一个非守护程序线程退出时,或者在调用exit(相当于System.exit)方法时,程序会正常退出,或者

  •   
  • 响应于用户中断(例如键入 Ctrl + C )或系统范围的事件(例如用户注销),虚拟机将终止或系统关闭。

  •   

所以我想,当插件关闭时,它会调用ShutdownHooks(如果我输入错了,请纠正我)。问题是我不了解如何在这两种可能性之间进行区分。我只想捕获信号并执行清理,但是如果其中一台VM关闭,我不想这样做。关于如何处理此问题的任何想法?

2 个答案:

答案 0 :(得分:0)

关闭挂钩的常见问题之一是没有确定的关闭顺序。在您的情况下,我假设<com.github.mikephil.charting.charts.PieChart android:id="@+id/fragment_chart" android:layout_width="match_parent" android:layout_height="400dp" /> 框架可能已经具有一个单独的shutdown钩子,该钩子已经执行了清除(停止任务)。

与关闭挂钩相关的javadoc在某种程度上解释了您不能依赖Schedule的存在,因为它们可能已经关闭了。

现在,当您尝试在关机挂钩中执行此操作时,可能会收到上述错误,具体取决于关机挂钩的执行顺序。


所有这些都不是您真正想要的,但是我想您可能根本没有问题。我宁愿讨论以下内容

  • 首先要使用关机钩的原因是什么?
  • 为什么(感觉)您需要自己停止正在运行的作业?

您可能需要添加在清理问题时运行的代码。根据我得到的信息,您可能想要关闭已经关闭的设备。


如果您仍然想进行Singal拦截,实际上有一种方法,但我不建议这样做。根据{{​​3}}这个链接,您可以使用sun.misc。*软件包的Signal类。

sun.misc软件包仅包含非官方的API,可以随时将其删除而无需另行通知。似乎没有官方的API支持您的用例(至少据我所知)。

答案 1 :(得分:0)

您可以覆盖SecurityManager checkExit(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);
        }
    }