如何在Java中使用Process正确读取shutdown hook

问题描述 投票:0回答:1

我在编写 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 outputstream shutdown-hook
1个回答
0
投票

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()
在返回之前会关闭所有流。

(尽管您在销毁进程之前创建了一个读取线程,但那是在一个单独的线程中。当线程启动时,您可能已经销毁了进程并关闭了其流。)

© www.soinside.com 2019 - 2024. All rights reserved.