什么时候应该用Java调用System.exit


193

在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? 
    }      
}

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

Answers:


207

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

“此方法永远不会正常返回。” 意味着该方法不会返回;一旦线程到达那里,它就不会回来。

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

由于某种原因,关闭挂钩似乎是一种被低估和误解的机制,并且人们正在使用各种专有的自定义黑客程序来重塑轮子,以退出其程序。我鼓励使用关闭钩子。无论如何,所有这些都在您将要使用的标准运行时中。


10
“此方法永远不会正常返回。” 意味着该方法不会返回;一旦线程到达那里,它就不会回来。特别要注意的是,这意味着您不能对测试System.exit(0)调用的方法进行单元测试...
Bill Michell 2010年

5
-1不正确。如果JVM正常终止,则无论是由于System.exit还是main()终止,关机挂钩都将运行。参见zx81 / doku / java / javadoc / j2se1.5.0 / docs / api / java / lang /…
sleske 2010年

31
@sleske:如果周围还有其他非守护进程线程,则main()的终止是不够的。只有在最后一个非守护程序线程终止后才启动关机,除非您显式调用System.exit()。运行时文档中对此进行了明确说明。
Joonas Pulakka

3
请注意,如果关闭钩子又依赖于名为System.exit的线程,则将导致死锁。
djechlin

8
补充一点,如果有人暂停运行时,则关闭挂钩将不会运行(Runtime.getRuntime().halt()
Rogue

50

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

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

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

要么引发异常,要么VM在返回之前终止。它永远不会“只是返回”。

值得致电System.exit()IME 的情况非常罕见。如果您正在编写命令行工具,并且想要通过退出代码指示错误,而不仅仅是抛出异常,那么这是有道理的。但是我不记得我上次在常规生产代码中使用它了。


2
编译器为什么不知道呢?System.exit()是否足够特殊以保证特定的检测代码?
Bart van Heukelom 2010年

3
@Bart:不,我不这么认为。将特殊情况放在这种情况下的语言中会增加语言的复杂性,而带来的好处却很小。
乔恩·斯基特

我认为“永远不会正常返回”与声明的“突然完成”有关。(JLS第14.1节)。我错了吗?
aioobe

@aioobe:不,你是对的。System.exit()将永远无法正常完成-它总是会完成并带有异常或关闭VM(JLS并未真正涵盖)。
乔恩·斯基特

1
@piechuckerr:是什么让你觉得呢?用非0的值来调用它是完全合理的,以向调用外壳程序(或任何其他程序)指示程序遇到错误。
乔恩·斯基特

15

该方法永远不会返回,因为这是世界的尽头,下一个代码也不会执行。

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

System.exit(42);

谁将使用您的退出代码?调用应用程序的脚本。在Windows,Unix和所有其他可编写脚本的环境中工作。

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

若要查看如何获取退出代码的值并将其用于UNIX shell脚本或Windows cmd脚本中,可以在此站点上查看此答案


14

System.exit(0)终止JVM。在像这样的简单示例中,很难理解差异。该参数被传递回操作系统,通常用于指示异常终止(例如,某种致命错误),因此,如果您从批处理文件或Shell脚本中调用Java,则可以获取该值并获得一个想法如果申请成功。

如果调用System.exit(0)了部署到应用程序服务器上的应用程序(在尝试之前先考虑一下),那将会产生很大的影响。


11

在可能具有复杂关闭挂钩的应用程序中,不应从未知线程中调用此方法。 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的问题。但是,它仅在某些条件下发生。我参加的一个Threadump并没有使我了解到哪些线程/关闭处理程序阻止了关闭。使用注释中描述的代码可以帮助我解决问题!

7

虽然答案确实很有帮助,但有些答案却遗漏了更多额外的细节。我希望以下内容除了上述答案之外,还可以帮助您了解Java中的关机过程:

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

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

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

7

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

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

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

  3. 诸如运行中的线程之类的编程问题通常不会被隐藏。
  4. 您可能会遇到不一致的应用程序状态,从而异常中断线程。(请参阅#3)

顺便说一句:如果您想表明程序异常终止,则返回非0的其他返回代码确实有意义。


2
“您应该始终完全控制系统状态。” 在Java中根本不可能做到这一点。IoC框架和应用服务器只是您放弃对系统状态的控制的两种非常流行和广泛使用的方式。那完全没问题。
mhlz 2015年

请在您的应用程序服务器中执行System.exit(0),并告诉我您的服务器管理员的反应...您错过了此问题的主题和我的答案。
oopexpert

也许我还不够清楚。您应该始终完全控制负责的系统状态。是的,一些职责已转移到应用程序服务器和IoC中。但是我不是在谈论那个。
oopexpert

5

需要System.exit

  • 当您想返回非零错误代码时
  • 当您想从非main()的地方退出程序时

在您的情况下,它执行的操作与简单的main-from-main完全相同。


你对后者意味着什么?关闭程序的正确方法是(很好)停止所有线程,这一步不必从主线程启动,因此这不是exit您唯一的选择。
Bart van Heukelom

@Bart:您所描述的是关闭程序的可能方法之一,而不是正确的方法。使用关闭钩子很好地停止所有线程没有任何错误。然后使用来启动关闭挂钩exit。这不是唯一的选择,但绝对是值得考虑的选择。
Joonas Pulakka 2010年

但是我从文档中收集到的信息,关闭挂钩非常微妙,可能没有很多时间来完成您希望它们执行的操作。不管怎样,使用它们可以在操作系统关闭或某些情况下很好地退出,但是如果您要自己调用System.exit(),我认为最好在关闭代码之前进行(此后您可能不希望这样做)需要System.exit()了)
Bart van Heukelom 2010年

1
关机钩确实会花费所有时间。在所有挂接返回之前,VM不会停止。通过注册需要很长时间才能执行的关闭挂钩,确实可以防止VM退出。但是当然可以用各种其他方式执行关闭序列。
Joonas Pulakka 2010年

如果还有其他非守护程序线程,则从main返回将不会关闭。
djechlin

4

Java语言规范

程序退出

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

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

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

这意味着您应该在拥有大型程序(最好至少大于此程序)并且要完成其执行时使用它。


3

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

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

如果要有意关闭完整的JVM,并且要利用其他答案中描述的可能性(例如,shutdown hooks:Java shutdown hook,命令行的非零返回值),请使用System.exit。调用:如何获取Windows批处理文件中Java程序的退出状态

还可以看看Runtime Exception:System.exit(num)还是从main抛出RuntimeException?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.