背景
項目需求中涉及java調用.bat文件進行圖像處理,先直接上簡略版程序
1 public void draw(){ 2 3 //調用bat腳本進行圖像處理 4 Process process = null; 5 InputStream in = null; 6 try { 7 process = Runtime.getRuntime().exec("startup.bat"); 8 9 //輸出測試 10 // in = process.getInputStream(); 11 // String line; 12 // BufferedReader br = new BufferedReader(new InputStreamReader(in)); 13 // while ((line = br.readLine()) != null) { 14 // System.out.println(line); 15 // } 16 17 //等待 18 process.waitFor(); 19 20 } catch (Exception e) { 21 22 } finally { 23 process.destroy(); 24 } 25 }
JAVA使用遇到的問題描述
一般需要調用系統命令時,大部分人第一反應肯定是使用Runtime.getRuntime().exec(command)返回一個process對象,再調用process.waitFor()來等待命令執行結束,獲取執行結果。
調試的時候發現異常現象,process.waitFor();一直沒有結束,導致線程阻塞再次,強行關閉程序后,發現圖像處理只進行了一部分。
根據現象並查看了JDK的幫助文檔,如下
如有必要,一直要等到由該 Process 對象表示的進程已經終止。如果已終止該子進程,此方法立即返回。但是直接調用這個方法會導致當前線程阻塞,直到退出子進程。
對此JDK文檔上還有如此解釋:因為本地的系統對標准輸入和輸出所提供的緩沖池有效,所以錯誤的對標准輸出快速的寫入何從標准輸入快速的讀入都有可能造成子進程的阻塞,甚至死鎖。
Process執行邏輯
* 主進程中調用Runtime.exec會創建一個子進程,用於執行腳本。子進程創建后會和主進程分別獨立運行。
* 創建的子進程沒有自己的終端或控制台。它的所有標准 io(即 stdin、stdout 和 stderr)操作都將通過三個流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父進程。父進程使用這些流來提供到子進程的輸入和獲得從子進程的輸出。
* 這時候子進程不斷向主進程發生數據,而主進程調用Process.waitfor后已掛起。當前子進程和主進程之間的緩沖區塞滿后,子進程不能繼續寫數據,然后也會掛起。
* 這樣子進程等待主進程讀取數據,主進程等待子進程結束,兩個進程相互等待,最終導致死鎖。
解決方法:在waitFor()之前,利用單獨兩個線程,分別處理process的getInputStream()和getErrorSteam(),防止緩沖區被撐滿,導致阻塞;
修改后代碼
1 public class test { 2 3 public void draw(){ 4 5 //調用bat腳本進行圖像處理 6 Process process = null; 7 InputStream in = null; 8 try { 9 process = Runtime.getRuntime().exec("startup.bat"); 10 11 //輸出測試 12 // in = process.getInputStream(); 13 // String line; 14 // BufferedReader br = new BufferedReader(new InputStreamReader(in)); 15 // while ((line = br.readLine()) != null) { 16 // System.out.println(line); 17 // } 18 //新啟兩個線程 19 new DealProcessSream(process.getInputStream()).start(); 20 new DealProcessSream(process.getErrorStream()).start(); 21 22 process.waitFor(); 23 24 } catch (Exception e) { 25 26 } finally { 27 process.destroy(); 28 } 29 } 30 }
1 public class DealProcessSream extends Thread { 2 private InputStream inputStream; 3 4 public DealProcessSream(InputStream inputStream) { 5 this.inputStream = inputStream; 6 } 7 8 public void run() { 9 InputStreamReader inputStreamReader = null; 10 BufferedReader br = null; 11 try { 12 inputStreamReader = new InputStreamReader( 13 inputStream); 14 br = new BufferedReader(inputStreamReader); 15 // 打印信息 16 // String line = null; 17 // while ((line = br.readLine()) != null) { 18 // System.out.println(line); 19 // } 20 // 不打印信息 21 while (br.readLine() != null); 22 } catch (IOException ioe) { 23 ioe.printStackTrace(); 24 }finally { 25 try { 26 br.close(); 27 inputStreamReader.close(); 28 } catch (IOException e) { 29 e.printStackTrace(); 30 } 31 } 32 33 } 34 }