Java 中默认未捕获信号处理程序策略的基本原理是什么?


关于

异常的智慧,特别是在Java中使用检查异常与未检查异常,已经写了很多,但我有兴趣看到对将线程终止作为默认策略而不是应用程序终止的决定的辩护 C++ 方式。 这个选择对我来说似乎非常危险:程序员没有随机计划的某些情况导致程序的某些部分在记录堆栈跟踪后死亡,但程序的其余部分坚决地继续,什么会出错? 我的直觉和经验表明,这里很多事情都可能出错,默认策略是那种应该由有特定原因选择它的人专门选择的东西,那么这种策略有什么好处,它有这么大的缺点? 我是否高估了风险?

编辑:根据到目前为止的答案,我觉得我需要更加专注于描述我所感知的危险;我说的是使用多个线程(例如在线程池中)来更新共享状态的应用程序的情况。 我认识到此策略不会给单线程应用程序带来问题。

EDIT2:从解释为什么 Thread.stop() 方法被弃用(在这里找到:http://docs.oracle.com/javase/7/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html),你可以看到语言维护者意识到了这些风险。 当线程由于未捕获的异常而意外死亡时,完全相同的问题也适用。 他们必须设计了 JVM,以便在线程死亡时自动解锁所有监视器,这似乎是一个糟糕的实现选择;在锁定监视器时让线程死亡应该表明整个程序应亡,因为替代方案几乎可以肯定是某些共享状态下的内部不一致。

@BD,不确定你的经验对此有何看法,因为您还没有在这里解释。但是,这是我作为开发人员的经历:

  1. 通常,如果应用程序的一个组件由于数据库重新启动或某些文件被替换等任何原因而失败(暂时或永久),则使应用程序失败是一个坏主意。 例如,如果我引入了一种新型的以旧换新系统并且出现了一些问题,它不应该关闭我的应用程序。

  2. 像 Web/应用程序服务器这样的应用程序应该能够继续工作并响应用户,即使它的任何部署都抛出任何奇怪的异常。

根据您对异常的担忧,通常所有应用程序都有一个运行状况监视系统,该系统监视其运行状况,例如CPU/磁盘/RAM使用情况或日志中的错误等,并相应地触发警报。

我希望这应该解决您的困惑。

通过与同事讨论这个问题并查看到目前为止收到的答案,我在这里形成了一个假设,并希望得到一些反馈。

我怀疑将这种行为设为默认行为的决定源于定义语言早期发展的哲学及其早期环境。

作为原始理念的一部分,程序员/设计人员应该使用检查异常,并且该语言强制要求可能由方法调用发出的检查异常(即已在方法定义中声明)必须在调用方法中处理,否则由它声明以"正式"将责任传递给更高级别的调用者。 通常的做法已经大大偏离了使用检查异常,更不用说实践中最常见的异常之一 NullPointerException 是未经检查的。 因此,程序员现在必须假设任何方法调用都可以生成未经检查的异常,其推论是,在并发上下文中更新共享数据的任何代码都必须实现这些更新的事务语义才能完全正确。 我的经验是,大多数开发人员并不真正理解这一点,即使他们确实了解多线程开发的基础知识,例如在通过同步管理关键部分时避免死锁。 默认的未捕获异常处理程序行为通过掩盖其影响来加剧问题:在C++中,未捕获的异常是否会导致损坏的共享状态并不重要,因为程序无论如何都已死,但在 Java 中,程序将继续尽其所能地跛行,尽管它很可能不再正常运行。

环境因素是,当语言首次开发时,单线程程序可能是常态,因此默认行为伪装成正确的行为。 多核架构的兴起和线程池使用量的增加更广泛地暴露了威胁,而通常应用的方法(例如使用不可变对象)只能解决它(提示某些人:ConcurrentMap 可能并不像您想象的那么安全)。 到目前为止,我的经验是,否认这种风险的人相对于其代码的实际安全要求不够偏执,但我希望被证明是错误的。

我怀疑修改未捕获的异常处理程序以终止程序应该是大多数开发组织所需的标准过程;至少应该为已知根据传入输入更新共享状态的线程池执行此操作。

正常(无 GUI,无容器)应用程序在未捕获的异常时退出 - 默认行为很好 - 与您想要的相同。

一个基于 GUI 的应用程序,最好显示错误消息并能够更有效地处理错误 - 例如,我们可以提交包含一些附加信息的缺陷报告。

通过提供特定于线程的异常处理程序(可能包括应用程序退出),该行为是完全可更改的。

以下是一些有用的说明

最新更新