Java Process與Runtime()的使用及調用cmd命令阻塞的解決方案
Java Process與Runtime()使用
java調用cmd執行bat文件有時會出現卡死的現象,當時感覺很迷惑,後來查資料,本來一般都是這樣來調用程序並獲取進程的輸出流的,但是我在windows上執行這樣的調用的時候卻總是在while那裡被堵塞瞭,結果造成ffmpeg程序在執行瞭一會後不再執行,這裡從官方的參考文檔中我們可以看到這是由於緩沖區的問題,由於java進程沒有清空ffmpeg程序寫到緩沖區的內容,結果導致ffmpeg程序一直在等待。
在網上也查找瞭很多這樣的問題,不過說的都是使用單獨的線程來進行控制,我也嘗試過很多網是所說的方法,可一直沒起什麼作用。
一直認為是getInputStream的緩沖區沒有被清空,不過問題確實是緩沖區的內容沒有被清空,但不是getInputStream的,而是getErrorStream的緩沖區,這樣問題就得到解決瞭。
所以我們在遇到java調用外部程序而導致線程阻塞的時候,可以考慮使用兩個線程來同時清空process獲取的兩個輸入流,如下這段程序:
public String excuteBatFile(String file, boolean isCloseWindow) { String cmdCommand = null; String res = null; if(isCloseWindow) { cmdCommand = "cmd.exe /c " + file; }else { cmdCommand = "cmd.exe /k " + file; } StringBuilder stringBuilder = new StringBuilder(); Process process = null; try { process = Runtime.getRuntime().exec(cmdCommand); final InputStream is1 = process.getInputStream(); new Thread(new Runnable() { public void run() { BufferedReader bufferedReader = null; String line = null; try { bufferedReader = new BufferedReader( new InputStreamReader(is1, "GBK")); while((line=bufferedReader.readLine()) != null) { stringBuilder.append(line+"\n"); } is1.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }).start(); // 啟動單獨的線程來清空p.getInputStream()的緩沖區 InputStream is2 = process.getErrorStream(); BufferedReader br2 = new BufferedReader(new InputStreamReader(is2)); StringBuilder buf = new StringBuilder(); // 保存輸出結果流 String line2 = null; while((line2 = br2.readLine()) != null) buf.append(line2); // log.info("----res:----" + stringBuilder + "&" + buf); return stringBuilder + "&" + buf; } catch (Exception e) { e.printStackTrace(); return e.toString(); } }
通過這樣我們使用一個線程來讀取process.getInputStream()的輸出流,使用另外一個線程來獲取process.getErrorStream()的輸出流,這樣我們就可以保證緩沖區得到及時的清空而不擔心線程被阻塞瞭。
當然根據需要你也可以保留process.getInputStream()流中的內容,這個就看調用的程序的處理瞭。
java Process執行cmd命令流阻塞處理
代碼如下:
public static void runCmd() { Process process = null; BufferedReader bufferedReader = null; try { Logger.getLogger(SystemService.class).info("============= 開始重啟機器 ============="); process = Runtime.getRuntime().exec("cmd.exe /c shutdown -r -f -t 0"); bufferedReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(process.getInputStream()), Charset.forName("GB2312"))); // 開啟線程讀取錯誤輸出,避免阻塞 new StreamInformatonThread(process.getErrorStream(), "error").start(); String outStr; while ((outStr = bufferedReader.readLine()) != null) { Logger.getLogger(SystemService.class).info("readLine -------> : " + outStr); } Logger.getLogger(SystemService.class).info("============= 重啟機器完成 ============="); } catch (IOException e) { Logger.getLogger(SystemService.class).error("============= 重啟機器失敗 ============="); e.printStackTrace(); } finally { if (process != null) { process.destroy(); } if (bufferedReader != null) { try { bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } } }
import java.io.*; /** * @Description:流阻塞處理 * @Author: zhangwenchao * @Date: 2019/7/9 11:35 */ public class StreamInformatonThread extends Thread { private InputStream is; private String str; private Logger logger = Logger.getLogger(StreamInformatonThread.class); public StreamInformatonThread(InputStream is, String str) { this.is = is; this.str = str; } public void run() { BufferedReader out = null; try { out = new BufferedReader(new InputStreamReader(is, "gbk")); String line; while ((line = out.readLine()) != null) { if (str.equals("error")) { logger.info("ErrorStream --------> :" +line); } else { logger.info("outLine ---------> :" + line); } } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Java中Process類的使用與註意事項說明
- 關於BufferedReader的讀取效率問題
- 基於Process#waitFor()阻塞問題的解決
- 解決Process.getInputStream()阻塞的問題
- 聊聊為什麼要使用BufferedReader讀取File