我們常在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();