异步运行进程并从stdout和stderr读取

Che*_*tah 12 java multithreading

我有一些代码运行一个进程,并从stdout和stderr异步读取,然后在进程完成时进行处理.它看起来像这样:

Process process = builder.start();

    Thread outThread = new Thread(() -> {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            // Read stream here
        } catch (Exception e) {
        }
    });

    Thread errThread = new Thread(() -> {
      try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
        // Read stream here
      } catch (Exception e) {
      }
    });

    outThread.start();
    errThread.start();

    new Thread(() -> {
      int exitCode = -1;
      try {
        exitCode = process.waitFor();
        outThread.join();
        errThread.join();
      } catch (Exception e) {
      }

    // Process completed and read all stdout and stderr here
    }).start();
Run Code Online (Sandbox Code Playgroud)

我的问题在于我使用3个线程来实现这个异步"run-and-get-output"任务 - 我不知道为什么,但我觉得使用3个线程感觉不对.我可以从线程池中分配线程,但这仍然会阻塞这些线程.

有什么我可以做的,也许是NIO,将这个减少到更少的(1?)线程?我能想到的任何东西都会不断地旋转一个线程(除非我添加几个睡眠),我真的不想这样做......

注意:我确实需要随时阅读(而不是在进程停止时)并且我确实需要将stdin与stderr分开,因此无法进行重定向.

Alc*_*zar 4

由于您已指定需要随时读取输出,因此没有非多线程解决方案。

不过,您可以将线程数量减少到主线程之外的一个:

Process process = builder.start();
Thread errThread = new Thread(() -> {
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
      // Read stream here
    } catch (Exception e) {
    }
});
errThread.start();

try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
        // Read stream here
} catch (Exception e) {
}
// we got an end of file, so there can't be any more input.  Now we need to wait for stderr/process exit.

int exitCode = -1;
try {
    exitCode = process.waitFor();
    errThread.join();
} catch (Exception e) {
}

// Process completed
Run Code Online (Sandbox Code Playgroud)

如果您确实不需要在进程结束之前处理错误/输出,您可以稍微简化一下,只使用主线程,如下所示:

    File stderrFile = File.createTempFile("tmpErr", "out");
    File stdoutFile = File.createTempFile("tmpStd", "out");
    try {
        ProcessBuilder builder = new ProcessBuilder("ls /tmp");
        Process p = builder.start();
        int exitCode = -1;
        boolean done = false;
        while (!done) {
            try {
                exitCode = p.waitFor();
                done = true;
            } catch (InterruptedException ie) {
                System.out.println("Interrupted waiting for process to exit.");
            }
        }
        BufferedReader err = new BufferedReader(new FileReader(stderrFile));
        BufferedReader in = new BufferedReader(new FileReader(stdoutFile));
        ....
    } finally {
        stderrFile.delete();
        stdoutFile.delete();
    }
Run Code Online (Sandbox Code Playgroud)

如果您从正在调用的进程中生成大量输出,这可能不是一个好主意,因为它可能会耗尽磁盘空间......但它可能会稍微快一些,因为它不必启动另一个线程。

  • 如果您实际上没有从 err 中读取内容,那么您的过程可能很快就会陷入困境。 (2认同)