什么时候应该在Java中调用System.exit
在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? } }
该文件说:“这个方法永远不会正常返回。” 这是什么意思?
System.exit()
可以用来在程序退出之前运行closures钩子 。 这是处理较大程序中closures的一种便捷方式,程序的所有部分都不能(也不应该)彼此知道。 然后,如果有人想要退出,他可以简单地调用System.exit()
,并closures挂钩(如果设置正确),照顾所有必要的closures仪式,如closures文件,释放资源等。
“这个方法永远不会正常返回。” 意味着方法不会返回; 一旦线程去那里,它不会回来。
另一种可能更常见的方法是退出程序,只是简单地达到main
方法的结束。 但是,如果有任何非守护线程正在运行,它们将不会closures,因此JVM不会退出。 因此,如果你有任何这样的非守护线程,你需要一些其他的手段(比closures钩子)closures所有非守护线程并释放其他资源。 如果没有其他非守护线程,则从main
返回将closuresJVM并调用closures钩子。
出于某种原因,关机挂钩似乎是一个被低估和误解的机制,人们正在重新发明各种专有的自定义黑客程序。 我会鼓励使用关机挂钩; 在标准的运行时间里 ,你仍然会使用它。
在这种情况下,这是不需要的。 没有额外的线程将被启动,你没有改变退出代码(默认为0) – 基本上没有意义。
当文档说方法永远不会正常返回,这意味着后续的代码行是无法实现的,即使编译器不知道:
System.exit(0); System.out.println("This line will never be reached");
要么抛出一个exception,要么在返回之前VM会终止。 它永远不会“只是返回”。
值得叫System.exit()
IME是非常罕见的。 如果你正在编写一个命令行工具,并且你希望通过退出代码来指示错误,而不是抛出一个exception,那么这可能是有意义的,但是我不记得我最后一次使用正常的生产代码。
System.exit(0)
终止JVM。 在这样的简单例子中,很难理解这种差异。 该参数被传回到操作系统,通常用于指示exception终止(例如某种致命的错误),所以如果你从batch file或shell脚本调用java,你将能够得到这个值,并得到一个想法如果申请成功。
如果您在部署到应用程序服务器的应用程序上调用System.exit(0)
,则会产生相当大的影响(请在尝试之前仔细考虑它)。
该方法永远不会返回,因为它是世界的尽头,你的代码将不会被执行。
在你的例子中,你的应用程序将退出代码中的同一位置,但是,如果你使用System.exit。 你可以select将自定义代码返回到环境中,比如说
System.exit(42);
谁将使用您的退出代码? 一个调用应用程序的脚本。 适用于Windows,Unix和所有其他脚本化环境。
为什么返回一个代码? 说“我没有成功”,“数据库没有回答”。
要了解如何获取退出代码的值并将其用于unix shell脚本或windows cmd脚本,可以在此站点上查看此答案
在可能有复杂closures挂钩的应用程序中,不应该从未知的线程调用此方法。 System.exit
永远不会正常退出,因为调用将会阻塞,直到JVM终止。 就好像任何运行的代码在完成之前都已经插上了电源插头。 调用System.exit
将启动程序的closures钩子,无论调用System.exit
线程将被阻塞,直到程序终止。 这意味着如果closures钩子依次向调用System.exit
的线程提交任务,程序将会死锁。
我在我的代码中处理这个如下:
public static void exit(final int status) { new Thread("App-exit") { @Override public void run() { System.exit(status); } }.start(); }
System.exit是必需的
- 当你想返回一个非零的错误代码
- 当你想从某个不是main()的地方退出程序时,
在你的情况下,它和简单的从主返回完全一样。
Java语言规范说这个
程序退出
当两件事情中的一件发生时,程序终止其所有活动并退出:
所有不是守护线程的线程都会终止。
某些线程调用Runtime类或System类的exit方法,安全pipe理器不禁止退出操作。
这意味着你应该使用它,当你有一个大的程序(以及最大的比这个更大),并且想要完成它的执行。
一个不应该调用System.exit(0)。
- 这是一个隐藏的“goto”,“gotos”打破了控制stream程。 在这种情况下依靠钩子是团队中的每个开发人员必须注意的心理映射。
- 退出程序“正常”将提供与System.exit(0)相同的退出代码到操作系统。 所以这是多余的。 如果你的程序不能“正常”退出,你的开发失控了。 您应始终完全控制系统状态。
- 您可以隐藏编程问题,例如运行未正常停止的线程。
- 这是指3:您可能会遇到一个不稳定的应用程序状态exception中断线程。
顺便说一句:如果您想要指示exception程序终止,返回比0更多的其他返回码是有意义的。
虽然答案真的很有帮助,但有些错过了一些额外的细节。 我希望下面有助于了解java中的closures过程,除了上面的答案:
- 在有序*closures中,JVM首先启动所有注册的closures钩子。 closures钩子是在Runtime.addShutdownHook中注册的未启动线程。
- JVM不保证closures钩子的启动顺序。 如果任何应用程序线程(守护进程或非守护进程)在closures时仍在运行, 它们将继续与closures进程同时运行 。
- 当所有closures钩子完成时,如果runFinalizersOnExit为true,JVM可能select运行终结器,然后暂停。
- JVM不会尝试停止或中断在关机时仍在运行的任何应用程序线程; 当JVM最终停止时,它们会突然终止。
- 如果closures挂钩或终结器没有完成,那么有序closures进程“挂起”,JVM必须突然closures。
- 在突然closures时,除了停止JVM以外,JVM不需要做任何事情; 关机挂钩将不会运行。
PS:JVM可以以有序或突然的方式closures。
- 当最后一个“正常”(非守护进程)线程终止,有人调用System.exit,或者通过其他特定于平台的方式(例如发送一个SIGINT或者敲击Ctrl-C)时,就会启动一个有序的closures。
- 虽然以上是closuresJVM的标准和首选方法,但也可以通过调用Runtime.halt或通过操作系统(例如发送SIGKILL)来终止JVM进程来突然closures它。
如果在JVM中运行另一个程序,并使用System.exit,那么第二个程序也将被closures。 想象一下,例如,您在群集节点上运行java作业,并且pipe理群集节点的java程序在相同的JVM中运行。 如果作业使用System.exit,则不仅会退出作业,还会“closures整个节点”。 由于pipe理程序意外closures,您将无法将其他作业发送到该群集节点。
因此,如果您希望能够从同一个JVM中的另一个Java程序控制程序,请不要使用System.exit。
使用System.exit如果您想要closures完整的JVM,并且希望利用其他解答中描述的可能性(例如closures钩子: Javaclosures钩子 ,命令行的非零返回值调用: 如何在Windowsbatch file中获取Java程序的退出状态 )。
也看看运行时exception: System.exit(num)或抛出一个RuntimeException从主?