问题描述
从Java产生和使用外部进程的流(IO)的正确方法是什么?据我所知,由于可能的缓冲区大小有限,因此应在与生成进程输入并行的线程中使用java结束输入流(进程输出).
What is the proper way to produce and consume the streams (IO) of external process from Java? As far as I know, java end input streams (process output) should be consumed in threads parallel to producing the process input due the possibly limited buffer size.
但是我不确定我是否最终需要与这些使用者线程进行同步,或者仅等待进程使用waitFor
方法退出就足以确定所有进程的输出实际上都被消耗了吗? I.E是否有可能,即使进程退出(关闭其输出流),流的Java端仍然有未读数据? waitFor
实际上怎么知道何时完成该过程?对于有问题的进程, EOF (关闭其输入流的java端)会发出信号以退出.
But I'm not sure if I eventually need to synchronize with those consumer threads, or is it enough just to wait for process to exit with waitFor
method, to be certain that all the process output is actually consumed? I.E is it possible, even if the process exits (closes it's output stream), there is still unread data in the java end of the stream? How does the waitFor
actually even know when the process is done? For the process in question, EOF (closing the java end of it's input stream) signals it to exit.
我当前处理流的解决方案如下
My current solution to handle the streams is following
public class Application {
private static final StringBuffer output = new StringBuffer();
private static final StringBuffer errOutput = new StringBuffer();
private static final CountDownLatch latch = new CountDownLatch(2);
public static void main(String[] args) throws IOException, InterruptedException {
Process exec = Runtime.getRuntime().exec("/bin/cat");
OutputStream procIn = exec.getOutputStream();
InputStream procOut = exec.getInputStream();
InputStream procErrOut = exec.getErrorStream();
new Thread(new StreamConsumer(procOut, output)).start();
new Thread(new StreamConsumer(procErrOut, errOutput)).start();
PrintWriter printWriter = new PrintWriter(procIn);
printWriter.print("hello world");
printWriter.flush();
printWriter.close();
int ret = exec.waitFor();
latch.await();
System.out.println(output.toString());
System.out.println(errOutput.toString());
}
public static class StreamConsumer implements Runnable {
private InputStream input;
private StringBuffer output;
public StreamConsumer(InputStream input, StringBuffer output) {
this.input = input;
this.output = output;
}
@Override
public void run() {
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String line;
try {
while ((line = reader.readLine()) != null) {
output.append(line + System.lineSeparator());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
reader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
latch.countDown();
}
}
}
}
}
是否有必要在这里使用锁存器,或者waitFor
隐含所有输出已被使用?另外,如果输出没有结束/不包含换行符,readLine
会丢失输出,还是仍然读取剩下的所有内容?读取空值是否意味着进程已关闭流的末尾-还有其他可能读取空值的情况吗?
Is it necessary to use the latch here, or does the waitFor
implicate all the output is already consumed? Also, if the output doesn't end/contain new line, will the readLine
miss the output, or still read all that is left? Does reading null mean process has closed it's end of the stream - is there any other scenario where null could be read?
处理流的正确方法是什么,我可以做比我的示例更好的事情吗?
What is the correct way to handle streams, could I do something better than in my example?
推荐答案
waitFor
表示进程已结束,但是您无法确定从stdout和stderr收集字符串的线程也已完成,因此使用闩锁是一种朝正确的方向迈进,但不是最佳选择.您不必等待闩锁,而可以直接等待线程:
waitFor
signals that the process ended, but you cannot be sure the threads which collect strings from its stdout and stderr finished also, so using a latch is a step in the right direction, but not an optimal one.Instead of waiting for the latch, you can wait for the threads directly:
Thread stdoutThread = new Thread(new StreamConsumer(procOut, output)).start();
Thread stderrThread = ...
...
int ret = exec.waitFor();
stdoutThread.join();
stderrThread.join();
顺便说一句,将行存储在StringBuffer
中是无用的工作.改用ArrayList<String>
,在不进行任何转换的情况下将行放置在那里,最后以循环的方式检索它们.
BTW, storing lines in StringBuffer
s is useless work. Use ArrayList<String>
instead, put lines there without any conversion, and finally retrieve them in a loop.
这篇关于Java exec方法,如何正确处理流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!