前段時間在工作中遇到這樣一個問題,java代碼中調用一個bat程序,在linux下執行完好,但是換到windows下就一直掛在那里不動了~
代碼如下:
public class CMDTest { public static void main(String[] args) { Process p = null; try { p = Runtime.getRuntime().exec("c:\\test.bat"); p.waitFor(); System.out.println(p.exitValue()); System.out.println("over"); } catch (Exception e) { e.printStackTrace(); } } }
其中,test.bat里就是一些命令操作,在這里就寫一個簡單的ping命令。
ping www.baidu.com
上面的代碼運行之后,等了半天都沒反應。Process的api中有如下說明:
ProcessBuilder.start() 和 Runtime.exec 方法創建一個本機進程,並返回 Process 子類的一個實例,該實例可用來控制進程並獲得相關信息。Process 類提供了執行從進程輸入、執行輸出到進程、等待進程完成、檢查進程的退出狀態以及銷毀(殺掉)進程的方法。 創建進程的方法可能無法針對某些本機平台上的特定進程很好地工作,比如,本機窗口進程,守護進程,Microsoft Windows 上的 Win16/DOS 進程,或者 shell 腳本。
創建的子進程沒有自己的終端或控制台。它的所有標准 io(即 stdin、stdout 和 stderr)操作都將通過三個流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父進程。父進程使用這些流來提供到子進程的輸入和獲得從子進程的輸出。
因為有些本機平台僅針對標准輸入和輸出流提供有限的緩沖區大小,如果讀寫子進程的輸出流或輸入流迅速出現失敗,則可能導致子進程阻塞,甚至產生死鎖。
也就是說:如果程序不斷在向標准輸出流和標准錯誤流寫數據,而JVM不讀取的話,當緩沖區滿之后將無法繼續寫入數據,最終造成阻塞在waitFor()這里。
這就是問題所在!之后我查了下網上的解決辦法,多數是創建兩個線程在waitFor()命令之前讀出窗口的標准輸出緩沖區和標准錯誤流的內容。
按照這個思路,我寫了如下util方法
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; public class CommandUtil { // 保存進程的輸入流信息 private List<String> stdoutList = new ArrayList<String>(); // 保存進程的錯誤流信息 private List<String> erroroutList = new ArrayList<String>(); public void executeCommand(String command) { // 先清空 stdoutList.clear(); erroroutList.clear(); Process p = null; try { p = Runtime.getRuntime().exec(command); // 創建2個線程,分別讀取輸入流緩沖區和錯誤流緩沖區 ThreadUtil stdoutUtil = new ThreadUtil(p.getInputStream(), stdoutList); ThreadUtil erroroutUtil = new ThreadUtil(p.getErrorStream(), erroroutList); //啟動線程讀取緩沖區數據 stdoutUtil.start(); erroroutUtil.start(); p.waitFor(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } public List<String> getStdoutList() { return stdoutList; } public List<String> getErroroutList() { return erroroutList; } } class ThreadUtil implements Runnable { // 設置讀取的字符編碼 private String character = "GB2312"; private List<String> list; private InputStream inputStream; public ThreadUtil(InputStream inputStream, List<String> list) { this.inputStream = inputStream; this.list = list; } public void start() { Thread thread = new Thread(this); thread.setDaemon(true);//將其設置為守護線程 thread.start(); } public void run() { BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(inputStream, character)); String line = null; while ((line = br.readLine()) != null) { if (line != null) { list.add(line); } } } catch (IOException e) { e.printStackTrace(); } finally { try { //釋放資源 inputStream.close(); br.close(); } catch (IOException e) { e.printStackTrace(); } } } }
再整個方法測試下:
import java.util.List; public class TestMain { public static void main(String[] args) { CommandUtil util = new CommandUtil(); util.executeCommand("c:\\test.bat"); printList(util.getStdoutList()); System.out.println("--------------------"); printList(util.getErroroutList()); } public static void printList(List<String> list){ for (String string : list) { System.out.println(string); } } }
這樣一來,問題確實解決了,再也不會出現阻塞了~