Java:如何在主应用程序停止时自动终止后台进程?



我正在尝试使用Java来启动一个Gradle进程,该进程在后台执行连续构建。下面是我的代码:

final var updateTimestampProcess =
new ProcessBuilder("gradlew.bat", "compileGroovy", "--continuous")
.directory(new File(System.getProperty("user.dir")))
.inheritIO()
try {
updateTimestampProcess.start()
} catch (IOException ignored) {}

它可以工作,但是当我使用IntelliJ的Stop命令时,该过程不会终止。我必须使用任务管理器来查找正在运行gradlew命令的Java进程,并手动结束它。

我如何修改我的代码,使后台进程终止时,主进程停止?

你可以使用一个关机钩子。

在shutdown钩子中添加一些代码来终止生成的进程。

Runtime
.getRuntime()
.addShutdownHook(new Thread(() -> System.out.println("killing the thing")));

进一步阅读:

https://www.baeldung.com/jvm-shutdown-hooks

如果您想使用外部脚本来终止进程,不久前我需要做类似的事情。当我启动我的应用程序,我写PID到一个文件,当我需要关闭它,我有这个脚本读取PID,然后杀死它:

#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PIDS_DIR="${SCRIPT_DIR}/pids/"
#
# Kills all the processes associated with a CWS ID
#
kill_cws() {
if [ $# -ne 0 ]; then
echo "ERROR: Invalid number of arguments. Expected 0."
exit 1
fi
echo "[INFO] Stopping CWS..."
for f in "${PIDS_DIR}/cws_"*".pid"; do
kill_cws_process "${f}"
done
echo "[INFO] CWS Stopped"
}
#
# Kills a specific process
#
kill_cws_process() {
local pid_file="$1"
local pid
pid=$(cat "${pid_file}")
echo "[INFO][PID=${pid}] Killing CWS instance"
kill -9 "${pid}"
ev=$?
if [ "${ev}" -ne 0 ]; then
echo "[WARN][PID=${pid}] Error killing PID"
fi
echo "[INFO][PID=${pid}] Waiting for instance termination"
echo "[INFO][PID=${pid}] Clean instance PID file"
rm "${pid_file}"
ev=$?
if [ "${ev}" -ne 0 ]; then
echo "[WARN][PID=${pid}] Error deleting pid file ${pid_file}"
fi
}
#
# MAIN
#
kill_cws "$@"

阅读了几个小时后,我意识到虽然这是可能的,但我还没有找到一个完全可行的解决方案。作为变通方法,我使用Gradle tools API设计了以下解决方案:

final ProjectConnection connection = GradleConnector
.newConnector()
.forProjectDirectory(new File(System.getProperty("user.dir")))
.connect()
try {
connection
.newBuild()
.forTasks("compileGroovy")
.withArguments("--continuous")
.setStandardOutput(System.out)
.run()
} catch (BuildException ignored) {
// Ignored for testing
}

它很简洁,它解决了所有的流程管理问题。

这个groovy脚本在主线程结束时终止外部进程。

不是说它很好,但它有效

def procBuilder =
new ProcessBuilder("notepad.exe")
.inheritIO()
def mainThread = Thread.currentThread()
//not a daemon to avoid termination..
Thread.start {
try {
println "daemon start"
def proc = procBuilder.start()
println "daemon process started"
mainThread.join()
println "daemon process killing..."
proc.destroy()
println "daemon process end"
} catch(e) {
println "daemon error: $e"
}
}
println "main thread sleep"
Thread.sleep(5000)
println "main thread end"

最新更新