【问题标题】:When should we call System.exit in Java我们什么时候应该在 Java 中调用 System.exit
【发布时间】:2011-04-12 13:44:00
【问题描述】:

在 Java 中,以下代码中有或没有 System.exit(0) 有什么区别?

public class TestExit
{      
    public static void main(String[] args)
    { 
        System.out.println("hello world");

        System.exit(0);  // is it necessary? And when it must be called? 
    }      
}

document 说:“这个方法永远不会正常返回。”这是什么意思?

【问题讨论】:

    标签: java exit


    【解决方案1】:

    在这种情况下,不需要。不会启动额外的线程,您不会更改退出代码(默认为 0) - 基本上没有意义。

    当文档说该方法永远不会正常返回时,这意味着后续代码行实际上无法访问,即使编译器不知道:

    System.exit(0);
    System.out.println("This line will never be reached");
    

    要么抛出异常,要么虚拟机在返回之前终止。它永远不会“返回”。

    很少有人值得致电System.exit() IME。如果您正在编写命令行工具,并且您想通过退出代码指示错误而不是仅仅抛出异常,这可能是有意义的......但我不记得我上次在正常生产代码中使用它是什么时候.

    【讨论】:

    • 编译器为什么不知道呢? System.exit() 还不够特殊,不足以保证特定的检测代码吗?
    • @Bart:不,我不这么认为。为此类事情在语言中添加特殊情况会增加语言的复杂性,但几乎没有什么好处。
    • 我认为“永远不会正常返回”与语句的“突然完成”有关。 (JLS 第 14.1 节)。我错了吗?
    • @aioobe:不,你是对的。 System.exit() 永远不会正常完成 - 它总是会在出现异常或关闭 VM 的情况下完成(JLS 中并未真正涵盖)。
    • @piechuckerr:是什么让你这么认为?使用 0 以外的值调用它是完全合理的,以向调用 shell(或其他)指示程序遇到错误。
    【解决方案2】:

    System.exit() 可用于在程序退出前运行shutdown hooks。这是在较大的程序中处理关闭的一种便捷方法,其中程序的所有部分都不能(也不应该)相互了解。然后,如果有人想退出,他可以简单地调用System.exit(),然后关闭挂钩(如果设置正确)负责执行所有必要的关闭仪式,例如关闭文件、释放资源等。

    "此方法永远不会正常返回。"仅表示该方法不会返回;一旦一个线程去那里,它就不会回来了。

    另一种可能更常见的退出程序的方法是简单地到达main 方法的末尾。但是如果有任何非守护线程在运行,它们不会被关闭,因此 JVM 也不会退出。因此,如果你有任何这样的非守护线程,你需要一些其他的方法(除了关闭钩子)来关闭所有非守护线程并释放其他资源。如果没有其他非守护线程,从main返回会关闭JVM并调用shutdown hook。

    出于某种原因,关闭挂钩似乎是一种被低估和被误解的机制,人们正在通过各种专有的自定义黑客重新发明轮子来退出他们的程序。我会鼓励使用关闭挂钩;这一切都在您将使用的标准Runtime 中。

    【讨论】:

    • "这个方法永远不会正常返回。"仅表示该方法不会返回;一旦一个线程去那里,它就不会回来了。请特别注意,这意味着您不能对进行 System.exit(0) 调用的方法进行单元测试...
    • -1 不正确。如果 JVM 正常终止,则关闭挂钩将运行,无论是因为 System.exit 还是 main() 终止。见zx81/doku/java/javadoc/j2se1.5.0/docs/api/java/lang/…
    • @sleske:如果周围有其他非守护线程,则终止 main() 是不够的。只有在最后一个非守护线程终止后才会启动关闭,除非您显式调用 System.exit()。这在运行时文档中有明确说明。
    • 请注意,如果您的关闭挂钩又依赖于调用 System.exit 的线程,您将死锁。
    • 添加一些东西,如果有人停止运行时,关闭挂钩将不会运行 (Runtime.getRuntime().halt())
    【解决方案3】:

    需要System.exit

    • 当您想要返回非 0 错误代码时
    • 当你想从不是 main() 的地方退出你的程序时

    在你的情况下,它与简单的从主返回完全相同的事情。

    【讨论】:

    • 后者是什么意思?关闭程序的正确方法是停止所有线程(很好),这一移动不必从主线程启动,所以exit 不是你唯一的选择。
    • @Bart:您所描述的是关闭程序的一种可能的方法,而不是正确的方法。使用关闭挂钩很好地停止所有线程并没有错。并使用exit 启动关闭挂钩。不是唯一的选择,但绝对是值得考虑的选择。
    • 但是我从文档中收集到的信息是,关闭挂钩非常微妙,可能没有太多时间去做你想让它们做的事情。无论如何,在操作系统关闭或其他情况下使用它们很好地退出,但如果你要自己调用 System.exit() 我认为最好在它之前执行关闭代码(之后你可能不会不再需要 System.exit())
    • 关闭钩子确实有它们完成的所有时间。在所有挂钩返回之前,VM 不会停止。确实可以通过注册一个需要很长时间才能执行的关闭挂钩来防止 VM 退出。但是,当然可以通过各种替代方式执行关机程序。
    • 如果有其他非守护线程,从main返回不会关闭。
    【解决方案4】:

    System.exit(0) 终止 JVM。在像这样的简单示例中,很难察觉差异。该参数被传递回操作系统,通常用于指示异常终止(例如某种致命错误),因此如果您从批处理文件或 shell 脚本调用 java,您将能够获取此值并了解如果申请成功。

    如果您在部署到应用程序服务器的应用程序上调用System.exit(0) 会产生相当大的影响(在尝试之前请考虑一下)。

    【讨论】:

      【解决方案5】:

      该方法永远不会返回,因为它是世界末日,您的任何代码都不会被执行。

      在您的示例中,您的应用程序无论如何都会在代码中的同一位置退出,但是,如果您使用 System.exit。您可以选择将自定义代码返回到环境中,例如

      System.exit(42);
      

      谁会使用您的退出代码?调用应用程序的脚本。适用于 Windows、Unix 和所有其他可编写脚本的环境。

      为什么要返回代码?说“我没有成功”、“数据库没有回答”之类的话。

      要了解如何获取退出代码的值并在 unix shell 脚本或 windows cmd 脚本中使用它,您可以查看this answer on this site

      【讨论】:

        【解决方案6】:

        Java Language Specification 这么说

        程序退出

        当发生以下两种情况之一时,程序会终止其所有活动并退出 发生:

        所有不是守护线程的线程都终止。

        某个线程调用了Runtime类或System类的exit方法, 并且退出操作未被安全管理器禁止。

        这意味着当你有大程序(嗯,至少比这个大)并且想要完成它的执行时,你应该使用它。

        【讨论】:

          【解决方案7】:

          在可能具有复杂关闭挂钩的应用程序中,不应从未知线程调用此方法。 System.exit 永远不会正常退出,因为调用将阻塞,直到 JVM 终止。就好像正在运行的任何代码都在完成之前拔掉了电源插头。调用System.exit 将启动程序的关闭钩子,任何调用System.exit 的线程都会阻塞直到程序终止。这意味着如果关闭钩子依次向调用System.exit的线程提交任务,程序将死锁。

          我正在使用以下代码在我的代码中处理此问题:

          public static void exit(final int status) {
              new Thread("App-exit") {
                  @Override
                  public void run() {
                      System.exit(status);
                  }
              }.start();
          }
          

          【讨论】:

          • 我还遇到了 System.exit 实际上不会结束 JVM 的问题。不过,它只发生在某些条件下。我采取的线程转储没有让我了解哪些线程/关闭处理程序正在阻止关闭。使用评论中描述的代码帮助我解决了它!
          【解决方案8】:

          如果您在 JVM 中运行另一个程序并且您使用 System.exit,则该第二个程序也将被关闭。例如,假设您在集群节点上运行 java 作业,并且管理集群节点的 java 程序在同一个 JVM 中运行。如果作业使用 System.exit,它不仅会退出作业,还会“关闭整个节点”。由于管理程序已意外关闭,您将无法向该集群节点发送另一个作业。

          因此,如果您希望能够从同一 JVM 中的另一个 java 程序控制您的程序,请不要使用 System.exit。

          如果您想故意关闭整个 JVM,并且如果您想利用其他答案中描述的可能性(例如关闭挂钩:Java shutdown hook,非零返回值,请使用 System.exit对于命令行调用:How to get the exit status of a Java program in Windows batch file)。

          还可以查看运行时异常:System.exit(num) or throw a RuntimeException from main?

          【讨论】:

            【解决方案9】:

            出于以下原因,切勿致电 System.exit(0)

            1. 这是一个隐藏的“goto”和“goto”中断控制流。在这种情况下依赖钩子是团队中每个开发人员都必须意识到的心理映射。
            2. “正常”退出程序会向操作系统提供与System.exit(0) 相同的退出代码,因此是多余的。

              如果您的程序无法“正常”退出,您就失去了对开发 [设计] 的控制。您应该始终完全控制系统状态。

            3. 正常运行的线程未停止等编程问题被隐藏。
            4. 您可能会遇到不一致的应用程序状态异常中断线程。 (参考#3)

            顺便说一句:如果您想指示程序异常终止,返回 0 以外的其他返回码确实有意义。

            【讨论】:

            • "您应该始终完全控制系统状态。"这在 Java 中根本不可能。 IoC 框架和应用程序服务器只是您放弃对系统状态的控制的两种非常流行和广泛使用的方式。这完全没问题。
            • 请在您的应用服务器中执行 System.exit(0) 并告诉我您的服务器管理员的反应...您错过了这个问题的主题和我的答案。
            • 也许我不够清楚。您应该始终完全控制您负责的系统状态。是的,一些职责转移到了应用服务器和 IoC。但我不是在说这个。
            【解决方案10】:

            虽然答案确实很有帮助,但它错过了一些额外的细节。除了上面的答案,我希望下面的内容能帮助理解java中的关机过程:

            1. 在有序*关闭中,JVM 首先启动所有已注册的关闭挂钩。关闭挂钩是向 Runtime.addShutdownHook 注册的未启动线程。
            2. JVM 不保证关闭挂钩的顺序 开始。如果任何应用程序线程(守护程序或非守护程序)仍在运行 在关机时,它们会继续与关机进程同时运行
            3. 当所有关闭挂钩都完成后,JVM 可能会选择运行终结器 如果 runFinalizersOnExit 为真,则停止。
            4. JVM 不会尝试停止或中断任何在关闭时仍在运行的应用程序线程;当 JVM 最终停止时,它们会突然终止。
            5. 如果关闭挂钩或终结器未完成,则有序关闭进程“挂起”并且必须突然关闭 JVM。
            6. 在突然关闭时,除了停止 JVM 之外,不需要 JVM 执行任何操作;关闭挂钩不会运行。

            PS:JVM 可以以有序突然的方式关闭。

            1. 当最后一个“正常”(nondaemon)线程终止、有人调用 System.exit 或通过其他特定于平台的方式(例如发送 SIGINT 或按 Ctrl-C)时,将启动有序关闭。
            2. 虽然以上是标准和首选方式 JVM要关闭,也可以通过调用Runtime.halt来突然关闭 或者通过操作系统杀死JVM进程(比如发送一个 SIGKILL)。

            【讨论】:

              猜你喜欢
              • 2016-11-11
              • 2016-10-10
              • 1970-01-01
              • 2021-09-07
              • 2017-03-07
              • 1970-01-01
              • 2020-09-06
              • 2011-07-04
              • 2017-09-13
              相关资源
              最近更新 更多