Java 程序如何检测到 MacOS 机器正在关闭?

DP_*_*DP_ 8 java macos daemon

我有一个 Java 应用程序,每当我启动机器时,我都想在我的 Mac 上启动它。每当机器关闭时,应用程序应正常关闭。

我在 main 方法中添加了一个关闭钩子:

Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownHook(scheduler)));
Run Code Online (Sandbox Code Playgroud)

ShutdownHook 类看起来像这样:

public class ShutdownHook implements  Runnable {
    private final static Logger LOGGER = LoggerFactory.getLogger("Shutdown Hook");
    private final Scheduler scheduler;

    public ShutdownHook(Scheduler scheduler) {
        this.scheduler = scheduler;
    }

    @Override
    public void run() {
        LOGGER.debug("Starting to shot down the application");
        try {
            scheduler.shutdown();
        } catch (final SchedulerException e) {
           LOGGER.error("An error occurred while trying to shut down the scheduler", e);
        }
        LOGGER.info("Exiting the shutdown hook");
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我将应用程序构建到一个可执行 JAR 中,并将其添加到“登录项”列表中。

“用户和组”窗口

该应用程序在我登录时启动。

但是,当机器关闭时,不会执行关闭钩子。

如何确保当我使用下面显示的菜单项关闭机器时,正在运行的 Java 应用程序检测到机器正在关闭?

“关机”菜单项

更新 1:

据我了解,修复错误的一种方法是使用launchd启动Java 程序。我写了plist下面的文件。

下一个问题是如何确保launchd运行该plist文件中描述的应用程序?

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>ExitTimeOut</key>
    <integer>300</integer>
    <key>StandardErrorPath</key>
    <string>/Users/XXXXXX/sw/wordcounter-daemon/error</string>
    <key>Label</key>
    <string>com.dpisarenko.wordcounterdaemon</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/java</string>
        <string>-jar</string>
        <string>/Users/XXXXXXX/sw/wordcounter-daemon/wordcounter-daemon-0.1-jar-with-dependencies.jar</string>
    </array>
</dict>
</plist>
Run Code Online (Sandbox Code Playgroud)

Kar*_*cki 3

通常 Unix 风格的系统将分两步关闭守护进程:

  1. SIGTERM信号发送到进程

  2. SIGKILL如果进程在第 1 点后 X 秒后仍在运行,则向进程发送信号

这个答案解释了如何ExitTimeOut在 MacOS 上配置控制这两个信号之间时间的值。

非守护进程的行为可能有所不同,这可能是您的进程未执行 JVM 关闭挂钩的原因。JVM 可能接收到SIGKILL并且根据设计,用户空间进程无法处理此信号。您可能希望将进程转换为守护进程。