我在编写 Java 代码时遇到一个问题,该代码将启动另一个 Unix 进程并打印其结果。 我已经完成并运行了积极的路线,但是当我终止进程时,问题发生了,我没有从关闭挂钩获得输出。我实际上知道关闭钩子会被输入,因为钩子有副作用,例如创建证明文件。
示例:
class JavaProgram1 {
public static void main(String[] args) {
registerShutdownHook(() -> {
writeTextFile("./shutdown", "ShutdownHook ran");
System.out.println("Hook shutdown");
});
System.out.println("Hello");
try { Thread.sleep(3600); } catch (InterruptedException e){}
System.out.println("Bye");
}
}
如果我使用 Java(或 IDEA IDE)从 cmd 行运行上面的代码片段(当然,使用正确实现的方法),它会完全按照预期工作。
我正在 RedHat Linux 上使用 Java 11。
当我想从另一个与此类似的 Java 程序启动该程序时,问题就开始了:
class JavaLauncher {
public static void main(String[] args){
Process process = startProcessThroughProcessBuilder("JavaProgram1");
Thread readingThread = readOutput(process);
process.destroy();
process.waitFor();
readingThread.join()
}
public void readOutput(Process process){
BufferedReader processOutput = new BufferedReader(new InputStreamReader(process.getInputStream()));
Thread readingThread = new Thread(() -> {
try{
String line;
while (line = processOutput.readLine() != null) {
logger.debug(line)
}
logger.info("IO finished");
} catch (IOException e) {
logger.warn("IO Interrupted: "+e)
}
})
}
}
我没有看到我期望的结果。
基本上可以归结为,当我在进程上调用 Destroy() 时,读取线程会抛出 IOException ,但我希望由于进程通过 shutdown hook 终止并且没有被杀死,因此它将关闭其流,发出信号EOF,读取线程应该干净地退出 while 循环。
相反,我收到了 IOException 抛出的消息,并显示“Stream close”消息。
读取线程甚至不会打印“Hook Shutdown”消息,表明在我尝试在 Hook 中写入该消息之前流已关闭。
如果我以关闭挂钩不包含任何输出的方式修改代码,则不会抛出 IOException,这使我认为流过早关闭。
您能给我一些关于可能发生的情况以及如何解决问题的指示吗?
Java 类
Process
比实际过程本身更多。 Java 类维护许多 IO 流作为字段。如果您单步执行 Process.destroy()
方法,您将看到它调用一个私有 destroy()
函数,该函数在终止进程后具有以下语句:
try { stdin.close(); } catch (IOException ignored) {}
try { stdout.close(); } catch (IOException ignored) {}
try { stderr.close(); } catch (IOException ignored) {}
因此,一旦您调用了
destroy()
,即使进程本身在死亡之前输出了某些内容,也不要指望能够从其输入流中读取数据,因为 destroy()
在返回之前会关闭所有流。
(尽管您在销毁进程之前创建了一个读取线程,但那是在一个单独的线程中。当线程启动时,您可能已经销毁了进程并关闭了其流。)