生活随笔
收集整理的這篇文章主要介紹了
Java Process中waitFor()的问题
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Java Process中waitFor()的問題 http://yearsaaaa123789.iteye.com/blog/1404865
? ? ? ? ??在編寫Java程序時,有時候我們需要調用其他的諸如exe,shell這樣的程序或腳本。在Java中提供了兩種方法來啟動其他程序:(1) 使用Runtime的exec()方法(2) 使用ProcessBuilder的start()方法 。Runtime和ProcessBulider提供了不同的方式來啟動程序,設置啟動參數、環境變量和工作目錄。但是這兩種方法都會返回一個用于管理操作系統進程的Process對象。這個對象中的waitFor()是我們今天要討論的重點。
? ? ? 來說說我遇到的實際情況:我想調用ffmpeg程序來對一首歌曲進行轉碼,把高音質版本的歌曲轉為多種低碼率的文件。但是在轉碼完成之后需要做以下操作:讀取文件大小,寫入ID3信息等。這時我們就想等轉碼操作完成之后我們可以知道。
如下這樣代碼
?
Java代碼 ?
Process?p?=?null ;?? try ?{??????p?=?Runtime.getRuntime().exec("notepad.exe" );?? }?catch ?(Exception?e)?{?? ????e.printStackTrace();?? }?? System.out.println("我想被打印..." );??
?在notepad.exe被執行的同時,打印也發生了,但是我們想要的是任務完成之后它才被打印。
之后發現在Process類中有一個waitFor()方法可以實現。如下:
?
Java代碼 ?
Process?p?=?null ;?? try ?{??????p?=?Runtime.getRuntime().exec("notepad.exe" );?? ????p.waitFor();?? }?catch ?(Exception?e)?{?? ????e.printStackTrace();?? }?? System.out.println("我想被打印..." );??
?這下又出現了這樣的現象,必須要等我們把記事本關閉打印語句才會被執行。并且你不碰手動關閉它那程序就一直不動,程序貌似掛了.....這是什么情況,想調用個別的程序有這么難嗎?讓我們來看看waitFor()的說明:
JDK幫助文檔上這么說:如有必要,一直要等到由該 Process 對象表示的進程已經終止。如果已終止該子進程,此方法立即返回。但是直接調用這個方法會導致當前線程阻塞,直到退出子進程。對此JDK文檔上還有如此解釋:因為本地的系統對標準輸入和輸出所提供的緩沖池有效,所以錯誤的對標準輸出快速的寫入何從標準輸入快速的讀入都有可能造成子進程的所,甚至死鎖。好了,問題的關鍵在緩沖區這個地方:可執行程序的標準輸出比較多,而運行窗口的標準緩沖區不夠大,所以發生阻塞。接著來分析緩沖區,哪來的這個東西,當Runtime對象調用exec(cmd)后,JVM會啟動一個子進程,該進程會與JVM進程建立三個管道連接:標準輸入,標準輸出和標準錯誤流。假設該程序不斷在向標準輸出流和標準錯誤流寫數據,而JVM不讀取的話,當緩沖區滿之后將無法繼續寫入數據,最終造成阻塞在waitfor()這里。 知道問題所在,我們解決問題就好辦了。查看網上說的方法多數是開兩個線程在waitfor()命令之前讀出窗口的標準輸出緩沖區和標準錯誤流的內容。代碼如下:
?
Java代碼 ?
Runtime?rt?=?Runtime.getRuntime();?? String?command?=?"cmd?/c?ffmpeg?-loglevel?quiet?-i?" +srcpath+"?-ab?" +bitrate+"k?-acodec?libmp3lame?" +desfile;?? try ?{???p?=?rt.exec(command?,null ,new ?File("C:\\ffmpeg-git-670229e-win32-static\\bin" ));?? ??? ?final ?InputStream?is1?=?p.getInputStream();??? ??? ?final ?InputStream?is2?=?p.getErrorStream();?? ??? ?new ?Thread()?{?? ????public ?void ?run()?{?? ???????BufferedReader?br1?=?new ?BufferedReader(new ?InputStreamReader(is1));?? ????????try ?{?? ????????????String?line1?=?null ;?? ????????????while ?((line1?=?br1.readLine())?!=?null )?{?? ??????????????????if ?(line1?!=?null ){}?? ??????????????}?? ????????}?catch ?(IOException?e)?{?? ?????????????e.printStackTrace();?? ????????}?? ????????finally {?? ?????????????try ?{?? ???????????????is1.close();?? ?????????????}?catch ?(IOException?e)?{?? ????????????????e.printStackTrace();?? ????????????}?? ??????????}?? ????????}?? ?????}.start();?? ???????????????????????????????? ???new ?Thread()?{??? ??????public ?void ??run()?{??? ???????BufferedReader?br2?=?new ??BufferedReader(new ??InputStreamReader(is2));??? ??????????try ?{??? ?????????????String?line2?=?null ?;??? ?????????????while ?((line2?=?br2.readLine())?!=??null ?)?{??? ??????????????????if ?(line2?!=?null ){}?? ?????????????}??? ???????????}?catch ?(IOException?e)?{??? ?????????????????e.printStackTrace();?? ???????????}??? ??????????finally {?? ?????????????try ?{?? ?????????????????is2.close();?? ?????????????}?catch ?(IOException?e)?{?? ?????????????????e.printStackTrace();?? ?????????????}?? ???????????}?? ????????}??? ??????}.start();???? ???????????????????????????????? ??????p.waitFor();?? ??????p.destroy();??? ?????System.out.println("我想被打印..." );?? ????}?catch ?(Exception?e)?{?? ????????????try {?? ????????????????p.getErrorStream().close();?? ????????????????p.getInputStream().close();?? ????????????????p.getOutputStream().close();?? ????????????????}?? ?????????????catch (Exception?ee){}?? ??????????}?? ???}??
?這個方法確實可以解決調用waitFor()方法阻塞無法返回的問題。但是在其中過程中我卻發現真正起關鍵作用的緩沖區是getErrorStream()說對應的那個緩沖區沒有被清空,意思就是說其實只要及時讀取標準錯誤流緩沖區的數據程序就不會被block。
?
Java代碼 ?
StringBuffer?sb?=?new ?StringBuffer();?? try ?{??Process?pro?=?Runtime.getRuntime().exec(cmdString);?? BufferedReader?br?=?new ?BufferedReader(new ?InputStreamReader(pro.getInputStream()),?4096 );?? String?line?=?null ;?? int ?i?=?0 ;??while ?((line?=?br.readLine())?!=?null )?{??if ?(0 ?!=?i)??sb.append("\r\n" );?? i++;?? sb.append(line);?? }?? }?catch ?(Exception?e)?{?? sb.append(e.getMessage());?? }?? return ?sb.toString();??
?
?不過這種寫法不知道是不是適合所有的情況,網上其他人說的需要開兩個線程可能不是沒有道理。這個還是具體問題具體對待吧。
?
到這里問題的原因也清楚了,問題也被解決了,是不是就結束了。讓我們回過頭來再分析一下,問題的關鍵是處在輸入流緩沖區那個地方,子進程的產生的輸出流沒有被JVM及時的讀取最后緩沖區滿了就卡住了。如果我們能夠不讓子進程向輸入流寫入數據,是不是可以解決這個問題。對于這個想法直接去ffmpeg官網查找,最終發現真的可以關閉子進程向窗口寫入數據。命令如下: ffmpeg.exe -loglevel quiet -i 1.mp3 -ab 16k -ar 22050 -acodec libmp3lame r.mp3 稍微分析一下 : -acodec? 音頻流編碼方式?-ab 音頻流碼率( 默認是同源文件碼率,也需要視codec 而定) -ar? 音頻流采樣率( 大多數情況下使用44100 和48000 ,分別對應PAL 制式和NTSC 制式,根據需要選擇),重點就是 -loglevel quiet 這句 ? http://www.ffmpeg.com.cn/index.php/Ffmpeg%E9%80%89%E9%A1%B9%E8%AF%A6%E8%A7%A3
?
這才是我們想要的結果:
?
Java代碼 ?
try ?{????p?=?Runtime.getRuntime().exec("cmd?/c?ffmpeg?-loglevel?quiet?-i?????D:\\a.mp3?-ab?168k?-ar?22050?-acodec?libmp3lame?D:\\b.mp3" ,null ,?? ????????????????????new ?File(?"C:\\ffmpeg-git-670229e-win32-static\\bin" ));?? ??p.waitFor();?? }?catch ?(Exception?e)?{?? ????e.printStackTrace();?? }?? System.out.println("我想被打印..." );??
?
最后是自己寫的一個簡單的操作MP3文件的類
?
Java代碼 ?
package ?com.yearsaaaa.util;???? import ?java.io.File;??import ?java.io.FileInputStream;??import ?java.math.BigDecimal;???? import ?javazoom.jl.decoder.Bitstream;??import ?javazoom.jl.decoder.Header;???? ? ? ? ? ? ?? public ?class ?MP3Util?{???????? ????? ? ?? ????public ?static ?double ?getMP3Size(String?path)?? ????{?? ????????File?file?=?new ?File(path);?? ????????double ?size?=?(double )file.length()/(1024 *1024 );?? ????????size?=?new ?BigDecimal(size).setScale(2 ,BigDecimal.ROUND_UP).doubleValue();?? ????????System.out.println("MP3文件的大小為:" +size);?? ????????return ?size;?? ????}?? ?????? ????? ? ? ?? ????public ?static ?String?getMP3Time(String?path)?? ????{?? ????????String?songTime?=?null ;?? ????????FileInputStream?fis?=?null ;?? ????????Bitstream?bt?=?null ;?? ????????File?file?=?new ?File(path);?? ????????try ?{?? ????????????fis?=?new ?FileInputStream(file);?? ????????????int ?b=fis.available();?? ????????????bt=new ?Bitstream(fis);?? ????????????Header?h=bt.readFrame();?? ????????????int ?time=(int )?h.total_ms(b);?? ????????????int ?i=time/1000 ;?? ????????????bt.close();?? ????????????fis.close();?? ????????????if (i%60 ?==?0 )?? ????????????????songTime?=?(i/60 +":" +i%60 +"0" );?? ????????????if (i%60 ?<10 )?? ????????????????songTime?=?(i/60 +":" +"0" +i%60 );?? ????????????else ?? ????????????????songTime?=?(i/60 +":" +i%60 );?? ????????????System.out.println("該歌曲的長度為:" +songTime);?? ????????}?? ????????catch ?(Exception?e)?{?? ????????????try ?{?? ????????????????bt.close();?? ????????????????fis.close();?? ????????????}?catch ?(Exception?ee)?{?? ????????????????ee.printStackTrace();?? ????????????}?? ????????}?? ????????return ?songTime;?? ????}?? ?????? ????? ? ? ? ? ? ? ?? ????public ?static ?void ?mp3Transcoding(String?srcPath,String?bitrate,String?desFile)?? ????{????? ?????????? ????????String?srcpath?=?srcPath.replace("?" ,?"\"?\"" );?? ????????String?desfile?=?desFile.replace("?" ,?"\"?\"" );?? ????????Runtime?rt?=?Runtime.getRuntime();?? ????????String?command?=?"cmd?/c?ffmpeg?-loglevel?quiet?-i?" +srcpath+"?-ab?" +bitrate+"k?-acodec?libmp3lame?" +desfile;?? ????????System.out.println(command);?? ????????Process?p?=?null ;?? ????????try {?? ?????????????? ????????????p?=?rt.exec(command?,null ,new ?File("C:\\ffmpeg-git-670229e-win32-static\\bin" ));?? ????????????p.waitFor();?? ????????????System.out.println("線程返回,轉碼后的文件大小為:" +desFile.length()+",現在可以做其他操作了,比如重新寫入ID3信息。" );?? ????????}?? ????????catch (Exception?e){?? ????????????e.printStackTrace();?? ????????????try {?? ????????????????p.getErrorStream().close();?? ????????????????p.getInputStream().close();?? ????????????????p.getOutputStream().close();?? ????????????????}?? ????????????catch (Exception?ee){}?? ????????}?? ????}?? ?????? ????public ?static ?void ?main(String[]?args)?{?? ?????????? ????????String[]?str?=?{"E:\\Kugou\\三寸天堂.mp3" };?? ????????for (String?s?:?str)?? ????????{?? ?????????????? ?????????????? ????????????File?f?=?new ?File(s);?? ????????????mp3Transcoding(f.getAbsolutePath(),"64" ,"d:\\chenmiao.mp3" );?? ????????}?? ????}?? }
總結
以上是生活随笔 為你收集整理的Java Process中waitFor()的问题 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。