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