我有一些代码运行一个进程,并从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();
我的问题在于我使用3个线程来实现这种异步" run-and-get-output"任务 - 我不知道为什么,但我觉得使用3个线程感觉不对。我可以从线程池中分配线程,但这仍然会阻塞这些线程。
我能做些什么,也许是NIO,可以将其减少到更少的(1?)线程?我能想到的任何东西都会不断地旋转一个线程(除非我加几个睡眠),我也不想做任何事情......
注意:我确实需要随时阅读(而不是在进程停止时)并且我确实需要将stdin与stderr分开,因此无法进行重定向。
答案 0 :(得分:3)
由于您已指定需要随时读取输出,因此没有非多线程解决方案。
您可以将主线程之外的线程数减少到一个:
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
如果你真的不需要在过程结束之前处理错误/输出,你可以简化一下,只使用你的主线程:
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();
}
如果您从正在调用的进程中生成大量输出可能不是一个好主意,因为它可能会耗尽磁盘空间......但它可能会稍微快一点,因为它不必旋转另一个线程。
答案 1 :(得分:1)
查看OstermillerUtils的ExecHelper。
这个想法是等待进程完成的线程,不仅仅是等待,而是在有输入可用的情况下从stdout和stderr读取输入,并且重新检查进程是否已经完成。
如果您没有使用stdout和stderr的输入进行任何繁重的处理,则可能不需要额外的线程来处理输入。只需复制ExecHelper并添加一些额外的函数/方法来处理任何新输入。我之前在流程运行时显示流程输出,这样做并不困难(但我丢失了源代码)。
如果确实需要一个单独的线程来处理输入,请确保在更新或读取这些缓冲区时同步输出和错误StringBuffers。
您可能想要考虑的另一件事是添加中止超时。实现起来有点困难,但对我来说非常有价值:如果一个过程需要花费太多时间,那么这个过程就会被破坏,从而确保没有任何东西悬而未决。你可以找到一个旧的(过时的?)示例this gist。
答案 2 :(得分:0)
你必须妥协。您可以选择以下选项:
一个。你可以用2个线程(而不是3个)来完成它:
第一个帖子:
stdout
读取,直到readline
返回null
Process.waitFor()
join
线程#2 第二个帖子:
stderr
读取,直到readline
返回null B中。合并流并使用Debian的annotate-output
来区分2个流
http://manpages.debian.org/cgi-bin/man.cgi?query=annotate-output&sektion=1
℃。如果它是一个短暂的过程,只需等待它的结束
d。如果它是一个长寿的过程,那么你可以在阅读器之间进行一些睡眠。
答案 3 :(得分:-1)
假设您不介意要合并的输入和错误流,您只能使用一个线程:
builder.redirectErrorStream(true); //merge input and error streams
Process process = builder.start();
Thread singleThread = new Thread(() -> {
int exitCode = -1;
//read from the merged stream
try (BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
//read until the stream is exhausted, meaning the process has terminated
while ((line = reader.readLine()) != null) {
System.out.println(line); //use the output here
}
//get the exit code if required
exitCode = process.waitFor();
} catch (Exception e) { }
}).start();