1、问题背景
使用java的Runtime.exec执行命令时一直没有返回,去掉命令的警告日志就OK。
2、问题原因
首先看下Process类的文档说明:

从这里可以看出,Runtime.exec()创建的子进程共用父进程的流,不同平台上,父进程的stream buffer可能被打满导致子进程阻塞,从而永远无法返回。
再分析我们的代码发现,process会输出两种stream:
1 2 |
process.getInputStream(); process.getErrorStream(); |
我们一种错误的写法是读取这两个stream的时候是顺序读取,导致getInputStream的标准输出流没读完,而 errStream就已经把stream buffer填满了,那么子进程被阻塞了。
正确的写法是这两种stream应该并行读取,那个stream有数据都会及时被读取,而不会导致stream buffer被写满。
3、标准解决方案
官方标准做法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
/** * Get the stderr/stdout outputs of a process and return when the process is done. * Both <b>must</b> be read or the process will block on windows. * * @param process The process to get the output from. * @param output The processOutput containing where to send the output. *Note that on Windows capturing the output is not optional. If output is null * the stdout/stderr will be captured and discarded. * @return the process return code. * @throws InterruptedException if {@link Process#waitFor()} was interrupted. */ private static int grabProcessOutput( @NonNull final Process process, @NonNull final ProcessOutput output) throws InterruptedException { Thread threadErr = new Thread("stderr") { @Override public void run() { InputStream stderr = process.getErrorStream(); OutputStream stream = output.getErrorOutput(); try { ByteStreams.copy(stderr, stream); stream.flush(); } catch (IOException e) { // ignore? } finally { try { Closeables.close(stderr, true /* swallowIOException */); } catch (IOException e) { // cannot happen } try { Closeables.close(stream, true /* swallowIOException */); } catch (IOException e) { // cannot happen } } } }; Thread threadOut = new Thread("stdout") { @Override public void run() { InputStream stdout = process.getInputStream(); OutputStream stream = output.getStandardOutput(); try { ByteStreams.copy(stdout, stream); stream.flush(); } catch (IOException e) { // ignore? } finally { try { Closeables.close(stdout, true /* swallowIOException */); } catch (IOException e) { // cannot happen } try { Closeables.close(stream, true /* swallowIOException */); } catch (IOException e) { // cannot happen } } } }; threadErr.start(); threadOut.start(); // it looks like on windows process#waitFor() can return // before the thread have filled the arrays, so we wait for both threads and the // process itself. threadErr.join(); threadOut.join(); // get the return code from the process return process.waitFor(); } |