如果从关闭挂钩中调用System.exit会发生什么?

13

我在Java中有一个相当复杂的关闭问题 - 有很多清理工作要做。 特别是,我正在尝试弄清楚如何从shutdown hook线程进行错误处理。 我目前的代码包括以下内容:

try {
    return shutdownPromise = doShutdown();
}
catch (Throwable exc) {
    logger.error("An exception was thrown while shutting down the application.", exc);  
    System.exit(1);
    return null;
}

当我最初写这篇文章时,我的想法是,在关闭时发生错误应该直接进入exit。但是exit并不是如此低级别的操作;它会调用关机挂钩。

因此,我思考了以下问题 -

  1. 从关机挂钩中调用exit会发生什么?
  2. 在关机挂钩中正确处理错误的方法是什么?

4
为什么不呢?实际上这是被鼓励的,分享知识非常重要。事实上,有一个复选框专门标注为“回答自己的问题”。 - tckmn
这是一个不错的答案,值得点赞。 - Bathsheba
1
好的,但只要你真正提出问题并自己或在他人的帮助下找到答案...不要在任何人回应之前就提出问题和回答。 - libik
3
@libik:错误的。http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/ - SLaks
1个回答

23
首先,简单的回答是:您的进程出现死锁。
System.exit 调用 Shutdown.exit,最终会以 这个代码块 结束。
synchronized (Shutdown.class) {
     /* Synchronize on the class object, causing any other thread
      * that attempts to initiate shutdown to stall indefinitely
      */
     sequence();
     halt(status);
}

这条评论是完全准确的。但要理解这一点,我们需要看看关闭挂钩(shutdown hook)的确切作用。

通过ApplicationShutdownHooks.add添加关闭挂钩,如这里的答案所述。请注意,上面的sequence方法最终会在Shutdown.runHooks中的这行代码上结束:

if (hook != null) hook.run();

首先,请注意这些钩子不是您的应用程序关闭钩子。它们是系统关闭钩子,不同之处在于其中一个负责确保您的应用程序关闭钩子运行。请注意,这是“运行”,而不是“启动”。换句话说,它会阻塞。其中一个关闭钩子将是一个系统钩子,它同时启动所有应用程序关闭钩子(您可以自己跟踪此代码或在链接的答案中找到),然后使用Thread.join调用阻塞,直到它们全部完成。其中一个将是您的应用程序关闭钩子-如果它调用System.exit,则将永远等待获取Shutdown.class上的锁。
接下来,愚蠢的答案:改为调用Runtime.getRuntime()。halt()方法。我之所以说“愚蠢的答案”是因为这可能是您想要执行的稍低级别的操作,而不是退出。如果System.exit表示失败的干净关闭,则halt是始终立即成功的硬关闭。

接下来,更明智的做法可能是什么都不做。只需让您的关闭挂钩完成即可,原因如下:您的关闭挂钩并不拥有关闭 - 特别是可能还有其他需要执行工作的关闭挂钩。相反,我怀疑这个挂钩的职责之一是最终强制进行硬关闭。我建议立即将其创建为延迟任务,然后正常完成此挂钩。


2
我很高兴通过一些困难的方式学到了这个。如果有人感兴趣,这里是案例研究:https://knotgillcup.blogspot.com/2019/08/systemexit-not-working.html - KnotGillCup

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