在編寫Java程序時,有時候我們需要調用其他的諸如exe,shell這樣的程序或腳本。在Java中提供了兩種方法來啟動其他程序:
(1) 使用Runtime的exec()方法
(2) 使用ProcessBuilder的start()方法
Runtime和ProcessBulider提供了不同的方式來啟動程序,設置啟動參數、環境變量和工作目錄。但是這兩種方法都會返回一個用於管理操作系統進程的Process對象。這個對象中的waitFor()是我們今天要討論的重點。
來說說我遇到的實際情況:我想調用ffmpeg程序來對一首歌曲進行轉碼,把高音質版本的歌曲轉為多種低碼率的文件。但是在轉碼完成之後需要做以下操作:讀取文件大小,寫入ID3信息等。這時我們就想等轉碼操作完成之後我們可以知道。
如下這樣代碼
Java代碼
收藏代碼
1.Process p = null;
2.try {
3. p = Runtime.getRuntime().exec("notepad.exe");
4.} catch (Exception e) {
5. e.printStackTrace();
6.}
7.System.out.println("我想被打印...");
在notepad.exe被執行的同時,打印也發生了,但是我們想要的是任務完成之後它才被打印。
之後發現在Process類中有一個waitFor()方法可以實現。如下:
Java代碼
收藏代碼
1.Process p = null;
2.try {
3. p = Runtime.getRuntime().exec("notepad.exe");
4. p.waitFor();
5.} catch (Exception e) {
6. e.printStackTrace();
7.}
8.System.out.println("我想被打印...");
這下又出現了這樣的現象,必須要等我們把記事本關閉, 打印語句才會被執行。並且你不能手動關閉它那程序就一直不動,程序貌似掛了.....這是什麼情況,想調用個別的程序有這麼難嗎?讓我們來看看waitFor()的說明:
JDK幫助文檔上這麼說:如有必要,一直要等到由該 Process 對象表示的進程已經終止。如果已終止該子進程,此方法立即返回。但是直接調用這個方法會導致當前線程阻塞,直到退出子進程。對此JDK文檔上還有如此解釋:因為本地的系統對標准輸入和輸出所提供的緩沖池有效,所以錯誤的對標准輸出快速的寫入何從標准輸入快速的讀入都有可能造成子進程的所,甚至死鎖。好了,
問題的關鍵在緩沖區這個地方:可執行程序的標准輸出比較多,而運行窗口的標准緩沖區不夠大,所以發生阻塞。
接著來分析緩沖區,哪來的這個東西,當Runtime對象調用exec(cmd)後,JVM會啟動一個子進程,該進程會與JVM進程建立三個管道連接:標准輸入,標准輸出和標准錯誤流。
假設該程序不斷在向標准輸出流和標准錯誤流寫數據,而JVM不讀取的話,當緩沖區滿之後將無法繼續寫入數據,最終造成阻塞在waitfor()這裡。 知道問題所在,我們解決問題就好辦了。查看網上說的方法多數是開兩個線程在waitfor()命令之前讀出窗口的標准輸出緩沖區和標准錯誤流的內容。代碼如下:
Java代碼
收藏代碼
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("我想被打印...");