【问题标题】:Run java.exe through ProcessBuilder leads to application hanging?通过 ProcessBuilder 运行 java.exe 导致应用程序挂起?
【发布时间】:2024-08-21 23:45:01
【问题描述】:

我有以下代码:

    ProcessBuilder pb = new ProcessBuilder("C:\\Program Files\\Java\\jdk1.8.0_111\\bin\\java", "-cp", "project_folder\\target\\classes", "package.ExternalProcess"); 
    Process p = pb.start();
    OutputStream processOutputStream = p.getOutputStream();
    IOUtils.write("1" + System.lineSeparator(), processOutputStream);
    InputStream processInputStream = p.getInputStream();
    System.out.println("--1--");
    System.out.println(process.isAlive()); // outputs true
    String result = IOUtils.toString(processInputStream, "UTF-8"); //<-- hangs here
    System.out.println("--2--");
    p.waitFor();
    System.out.println(result);

外部流程来源:

public class ExternalProcess {
    public static void main(String[] args) {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String input = null;
        try {
            input = br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("processed[" + input + "]");
    }
}

第一个代码产生
--1-- true

并坚持下去

如何更正程序以及为什么会挂起?

附言

当我尝试写作时

java -cp project_folder\target\classes package.ExternalProcess

当我向控制台写入内容并返回预期结果时,它从 cmd 等待

【问题讨论】:

  • "java" 应该是 java 的完整路径,例如 "/usr/bin/java"
  • @Aardvocate Akintayo Olusegun,更正了问题。没有帮助
  • @Aardvocate Akintayo Olusegun,反正我已经正确设置了 PATH 环境变量

标签: java process stdout stdin processbuilder


【解决方案1】:

问题在于缓冲,特别是Process.getOutputStream() 的缓冲。如果我添加:

processOutputStream.flush();

之后:

IOUtils.write("1" + System.lineSeparator(), processOutputStream);

它按预期工作。 IOUtils.write 写入processOutputStream,只是将数据保存在父进程的缓冲区中,但数据实际上并没有通过连接发送到子进程,所以子进程挂在br.readLine(),等待永远不会出现的数据。 flush() 调用将数据从父级缓冲区发送到可以读取的子级。

(您可以在 Process.getOutputStream() 的 javadocs 中看到它说:

实现说明:对返回的输出流进行缓冲是个好主意。

虽然没有具体说标准实现是缓冲的,但假设 Oracle 将遵循他们自己的实现建议是合理的。)

如果您不想在每次写入后都调用flush(),您可以将OutputStream 包裹在PrintStream 中,并开启autoFlush

PrintStream processPrintStream = new PrintStream(processOutputStream, true);

那么不要使用IOUtils.write,只需使用PrintStream 方法:

processPrintStream.println("1");

因为autoFlushPrintStream 中处于打开状态,所以无论何时调用println 方法之一(或写入字节数组或换行符),PrintStream 都会在底层OutputStream 上调用flush()给你。

【讨论】:

    最近更新 更多