我们常在java中运行第三方程序,如sh、python,java提供一个Runtime.exec()方法,生成一个Process对象。今天在使用这个方法的时候,发现接口半天没有返回数据。查了一下,原来还有这样的一个坑。记录一下
代码是网上的,见如下。
public static String execCmd(String cmd, File dir) throwsException {
StringBuilder result= newStringBuilder();
Process process= null;
BufferedReader bufrIn= null;
BufferedReader bufrError= null;try{//执行命令, 返回一个子进程对象(命令在子进程中执行)
process = Runtime.getRuntime().exec(cmd, null, dir);//方法阻塞, 等待命令执行完成(成功会返回0)
process.waitFor();//获取命令执行结果, 有两个结果: 正常的输出 和 错误的输出(PS: 子进程的输出就是主进程的输入)
bufrIn = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
bufrError= new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));//读取输出
String line = null;while ((line = bufrIn.readLine()) != null) {
result.append(line).append('\n');
}while ((line = bufrError.readLine()) != null) {
result.append(line).append('\n');
}returnresult
}
执行后,一直没有输出。原因如下:
1. 主进程中调用Runtime.exec会创建一个子进程,用于执行shell脚本。子进程创建后会和主进程分别独立运行。
2. 因为主进程需要等待脚本执行完成,然后对脚本返回值或输出进行处理,所以这里主进程调用Process.waitfor等待子进程完成。
3. 通过shell脚本可以看出:子进程执行过程就是不断的打印信息。主进程中可以通过Process.getInputStream和Process.getErrorStream获取并处理。
4. 这时候子进程不断向主进程发生数据,而主进程调用Process.waitfor后已挂起。当前子进程和主进程之间的缓冲区塞满后,子进程不能继续写数据,然后也会挂起。
5. 这样子进程等待主进程读取数据,主进程等待子进程结束,两个进程相互等待,最终导致死锁
解决方案:
开两个线程在waitfor()命令之前读出窗口的标准输出缓冲区和标准错误流的内容。
newThread() {
@Overridepublic voidrun() {
BufferedReader in= new BufferedReader(newInputStreamReader(process.getInputStream()));
String line= null;try{while ((line = in.readLine()) != null) {
log.info("datax执行的结果为: "+line);
}
}catch(IOException e) {
e.printStackTrace();
}finally{try{
in.close();
}catch(IOException e) {
e.printStackTrace();
}
}
}
}.start();newThread(){
@Overridepublic voidrun()
{
BufferedReader err= new BufferedReader(newInputStreamReader(process.getErrorStream()));
String line= null;
StringBuilder result=newStringBuilder();try{while((line = err.readLine()) != null)
{
result.append(line);
}
}catch(IOException e)
{
e.printStackTrace();
}finally{try{
err.close();}catch(IOException e)
{
e.printStackTrace();
}
}
}
}.start();