【问题标题】:How to gracefully shutdown a Java application that is terminated as a result of closing the command line from which it was executed?如何优雅地关闭因关闭执行它的命令行而终止​​的 Java 应用程序?
【发布时间】:2014-11-27 07:49:55
【问题描述】:

Best Way to Gracefully Shutdown a Java Command Line Program 上有一个已回答的问题。当程序被 Ctrl+C 终止时,shutdown hook 会完成这项工作。

我的问题是,如果在 Java 程序执行期间命令行本身关闭,如何优雅地退出?我用关闭挂钩进行了测试,但在这种情况下它不起作用。我无法找出在这种情况下虚拟机会发生什么。该进程及其所有线程是否立即终止? 关闭命令行会产生什么样的信号?

那么,如何解决这个特殊问题?

编辑:问题与 Windows 环境有关。

【问题讨论】:

标签: java windows command-line exit


【解决方案1】:

从逻辑上讲,应该引发 SIGHUP(终端挂断)。

实际上,我只是用一个简单的 shell 脚本检查了我的猜测。是的,当用户关闭启动应用程序的终端仿真器(并且未从中分离)时,应用程序会收到 SIGHUP。所以设置一个 SIGHUP 处理程序并做出相应的反应。通常的行为是终止应用程序,但您的意图可能不同。

此外,如果您的 Java 应用程序执行任何 STDIN/STDOUT 操作,则应在收到 HUP 时将其关闭或至少重新配置,因为尝试从不存在的终端读取/写入会导致 SIGPIPE 和/或程序块.

对于 Windows,请查看 Catch Windows terminal closing on running process

【讨论】:

  • 我编辑了我的问题。最初,我混合了基于 Linux 和 Windows 的 kill 命令。实际上,我对这种场景的 Windows 版本特别感兴趣。
  • 是的,我指的是 cmd.exe
【解决方案2】:

编辑windows环境:

我对windows环境没有太多经验,但是如果你想让你的应用程序继续运行,它通常被部署为windows服务(它类似于linux上的守护进程)。您通常会通过列出所有服务的实用程序启动/停止/重新启动服务(我认为您可以通过控制面板 -> 管理工具 -> 服务来访问它。我猜想通过这个工具发出“停止”信号会发出优雅的信号关闭。如果您通过任务管理器终止服务,则不会是正常关闭。


这是基于 Linux 还是基于 Windows 的环境?在 Linux 中,如果您在后台运行程序(并使用“exit”命令退出 shell),它将继续运行。您可以通过在末尾添加 & 将您的应用程序置于后台。此外,许多应用程序/服务在后台运行。如果您使用 startup.sh 命令执行 Tomcat 启动脚本,即使您退出启动它的终端,它也会继续在后台运行。在 Windows 上,这个概念也应该类似。

在关闭应用程序方面,您在 Linux 系统上使用 kill 命令。进程上的 kill 命令向它发送 SIGTERM 信号。应用程序可以实现代码来拦截 SIGTERM 并在检测到 SIGTERM 时正常关闭。如果应用程序没有优雅地处理 SIGTERM,那么它不会响应 SIGTERM / kill。在这种情况下,您需要显式地给它一个 SIGKILL (kill -9) 来强行杀死它。在这种情况下,无法正常关机。

【讨论】:

  • "如果您在后台运行程序,即使您关闭终端,它也会继续运行" 不一定如此。
  • 不要忘记 nohup,没有它你的建议不会是真的。
  • 抱歉,我在谈论 SIGKILL 和 SIGTERM 时误导了我的问题。实际上,我感兴趣的是 Windows 环境,并且该进程似乎没有在后台继续运行。
  • " 如果您在后台运行程序(并使用 'exit' 命令退出 shell),它将继续运行。"这又不是真的,取决于 shell 默认设置。默认情况下,bash 使用 & 分离后台运行的程序,zsh 没有。
【解决方案3】:

在 Java 中,有一个特殊的 Runtime 方法:addShutdownHook

这允许您初始化 JVM 将在停止之前尝试运行的线程。它是 放置您想要执行的任何清理的地方,即使在 Ctrl-C 关闭父窗口的情况下也是如此。摘自 javadoc : 关闭钩子只是一个已初始化但未启动的线程。当虚拟机开始其关闭序列时,它将以某种未指定的顺序启动所有已注册的关闭挂钩并让它们同时运行。当所有钩子都完成后,如果 finalization-on-exit 已启用,它将运行所有未调用的终结器。最后,虚拟机将停止。

即使程序正常结束,也会调用关闭挂钩。在这种情况下,在使用removeShutdownHook 退出之前删除注册的钩子会更干净(仍然是Runtime 的方法)

编辑:

在Windows环境下,没有真正的信号,只有系统关闭时的特殊回调。 AFAIK,在这种情况下正确调用了系统挂钩,但我承认我从未真正测试过。在 Windows 中,可以通过 2 种方式要求进程终止:

  • PostQuitMessage 函数在进程事件循环中发布 WM_QUIT 消息 - 通常进程应该退出,但它可以进行清理(相当于 Unix SIG_TERM)
  • TerminateProcess 立即停止进程及其所有线程(相当于 Unix SIG_KILL)

控制台进程可以使用可以拦截 Ctrl-C、Ctrl-Break 或 Ctrl-Close 事件的 ConsoleControlHandler。前两个是通过键盘生成的,最后一个是在用户关闭控制台时生成的。但通常情况下,Oracle JVM 在获取与 SIGTERM 处理相同的 Ctrl-Close 事件时应该使用系统挂钩机制。

【讨论】:

  • 我在我的问题中提到了关机钩子。它在 Ctrl-C 的情况下有效,但在关闭 cmd.exe 的情况下无效
猜你喜欢
  • 2010-11-04
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
  • 2016-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多