前些天使用Java调用外部程序的时候
发现线程会堵塞在waitfor()方法
调用方法如下
Process process = RuntimegetRuntime()exec(cmd)
processwaitfor()
如果直接在Shell中调用这个程序程序会很快结束不会僵死
为什么会堵塞呢原因是当调用exec(cmd)后JVM会启动一个子进程该进程会与JVM进程建立个管道连接标准输入标准输出和标准错误流假设该程序不断在向标准输出流和标准错误流写数据而JVM不读取数据会暂时缓沖在Linux的缓沖区缓沖区满后该程序将无法继续写数据会僵死所以Java程序就会僵死在waitfor()永远无法结束
解决办法就是增加两个线程一个线程负责读标准输出流另一个负责读标准错误流这样子数据就不会积压在缓沖区程序就能够顺利运行
查看源代码后还发现一个潜在的问题但程序执行到exec的时候JVM会使用管道占有个文件句柄但程序运行结束后这三个句柄并不会自动关闭这样最终会导致javaioIOException: Too many open files所以就算外部程序的没有输出也必须关闭句柄
Process process=null;
try{
process = RuntimegetRuntime()exec(cmd)
processwaitfor()
}cache{
processgetOutputStream()close()
processgetInputStream()close()
processgetErrorStream()close()
}
我们发觉当调用close()方法后JVM并不会立即回收句柄具体的回收时间不确定另外如果不调用close()句柄也会被回收也可能发生Too many open files的错误根据这篇文章不同的垃圾收集器会选择不同的回收策略所以最好还是要关闭
总结
如果外部程序有大量输出需要启动额外的线程来读取标准输出和标准错误流
必须关闭三个句柄