摘錄自:http://lelglin.iteye.com/blog/1487351
JDK幫助文檔上說:如有必要,一直要等到由該 Process 對象表示的進程已經終止。如果已終止該子進程,此方法立即返回。但是直接調用這個方法會導致當前線程阻塞,直到退出子進程。對此JDK文檔上還有如此解釋:因為本地的系統對標准輸入和輸出所提供的緩沖池有效,所以錯誤的對標准輸出快速的寫入何從標准輸入快速的讀入都有可能造成子進程的所,甚至死鎖。好了,
問題的關鍵在緩沖區這個地方:可執行程序的標准輸出比較多,而運行窗口的標准緩沖區不夠大,所以發生阻塞。
接着來分析緩沖區,哪來的這個東西,當Runtime對象調用exec(cmd)后,JVM會啟動一個子進程,該進程會與JVM進程建立三個管道連接:標准輸入,標准輸出和標准錯誤流。
假設該程序不斷在向標准輸出流和標准錯誤流寫數據,而JVM不讀取的話,當緩沖區滿之后將無法繼續寫入數據,最終造成阻塞在waitfor()這里。 知道問題所在,我們解決問題就好辦了。查看網上說的方法多數是開兩個線程在waitfor()命令之前讀出窗口的標准輸出緩沖區和標准錯誤流的內容。代碼如下:
1 Runtime rt = Runtime.getRuntime(); 2 String command = "cmd /c ffmpeg -loglevel quiet -i "+srcpath+" -ab "+bitrate+"k -acodec libmp3lame "+desfile; 3 try { 4 p = rt.exec(command ,null,new File("C:\\ffmpeg-git-670229e-win32-static\\bin")); 5 //獲取進程的標准輸入流 6 final InputStream is1 = p.getInputStream(); 7 //獲取進城的錯誤流 8 final InputStream is2 = p.getErrorStream(); 9 //啟動兩個線程,一個線程負責讀標准輸出流,另一個負責讀標准錯誤流 10 new Thread() { 11 public void run() { 12 BufferedReader br1 = new BufferedReader(new InputStreamReader(is1)); 13 try { 14 String line1 = null; 15 while ((line1 = br1.readLine()) != null) { 16 if (line1 != null){} 17 } 18 } catch (IOException e) { 19 e.printStackTrace(); 20 } 21 finally{ 22 try { 23 is1.close(); 24 } catch (IOException e) { 25 e.printStackTrace(); 26 } 27 } 28 } 29 }.start(); 30 31 new Thread() { 32 public void run() { 33 BufferedReader br2 = new BufferedReader(new InputStreamReader(is2)); 34 try { 35 String line2 = null ; 36 while ((line2 = br2.readLine()) != null ) { 37 if (line2 != null){} 38 } 39 } catch (IOException e) { 40 e.printStackTrace(); 41 } 42 finally{ 43 try { 44 is2.close(); 45 } catch (IOException e) { 46 e.printStackTrace(); 47 } 48 } 49 } 50 }.start(); 51 52 p.waitFor(); 53 p.destroy(); 54 System.out.println("我想被打印..."); 55 } catch (Exception e) { 56 try{ 57 p.getErrorStream().close(); 58 p.getInputStream().close(); 59 p.getOutputStream().close(); 60 } 61 catch(Exception ee){} 62 } 63 }
這個方法確實可以解決調用waitFor()方法阻塞無法返回的問題。但是在其中過程中我卻發現真正起關鍵作用的緩沖區是getErrorStream()所對應的那個緩沖區沒有被清空,意思就是說其實只要及時讀取標准錯誤流緩沖區的數據程序就不會被block。
1 StringBuffer sb = new StringBuffer(); 2 try { 3 Process pro = Runtime.getRuntime().exec(cmdString); 4 BufferedReader br = new BufferedReader(new InputStreamReader(pro.getInputStream()), 4096); 5 String line = null; 6 int i = 0; 7 while ((line = br.readLine()) != null) { 8 if (0 != i) 9 sb.append("\r\n"); 10 i++; 11 sb.append(line); 12 } 13 } catch (Exception e) { 14 sb.append(e.getMessage()); 15 } 16 return sb.toString();
不過這種寫法不知道是不是適合所有的情況,網上其他人說的需要開兩個線程可能不是沒有道理。這個還是具體問題具體對待吧。