您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關 如何解決java調用process線程阻塞的問題,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
項目需求中涉及java調用.bat文件進行圖像處理,先直接上簡略版程序
public void draw(){ //調用bat腳本進行圖像處理 Process process = null; InputStream in = null; try { process = Runtime.getRuntime().exec("startup.bat"); //輸出測試 // in = process.getInputStream(); // String line; // BufferedReader br = new BufferedReader(new InputStreamReader(in)); // while ((line = br.readLine()) != null) { // System.out.println(line); // } //等待 process.waitFor(); } catch (Exception e) { } finally { process.destroy(); } }
一般需要調用系統命令時,大部分人第一反應肯定是使用Runtime.getRuntime().exec(command)返回一個process對象,再調用process.waitFor()來等待命令執行結束,獲取執行結果。
調試的時候發現異常現象,process.waitFor();一直沒有結束,導致線程阻塞再次,強行關閉程序后,發現圖像處理只進行了一部分。
根據現象并查看了JDK的幫助文檔,如下
如有必要,一直要等到由該 Process 對象表示的進程已經終止。如果已終止該子進程,此方法立即返回。但是直接調用這個方法會導致當前線程阻塞,直到退出子進程。
對此JDK文檔上還有如此解釋:因為本地的系統對標準輸入和輸出所提供的緩沖池有效,所以錯誤的對標準輸出快速的寫入何從標準輸入快速的讀入都有可能造成子進程的阻塞,甚至死鎖。
* 主進程中調用Runtime.exec會創建一個子進程,用于執行腳本。子進程創建后會和主進程分別獨立運行。
* 創建的子進程沒有自己的終端或控制臺。它的所有標準 io(即 stdin、stdout 和 stderr)操作都將通過三個流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父進程。父進程使用這些流來提供到子進程的輸入和獲得從子進程的輸出。
* 這時候子進程不斷向主進程發生數據,而主進程調用Process.waitfor后已掛起。當前子進程和主進程之間的緩沖區塞滿后,子進程不能繼續寫數據,然后也會掛起。
* 這樣子進程等待主進程讀取數據,主進程等待子進程結束,兩個進程相互等待,最終導致死鎖。
在waitFor()之前,利用單獨兩個線程,分別處理process的getInputStream()和getErrorSteam(),防止緩沖區被撐滿,導致阻塞;
修改后代碼
public class test { public void draw(){ //調用bat腳本進行圖像處理 Process process = null; InputStream in = null; try { process = Runtime.getRuntime().exec("startup.bat"); //輸出測試 // in = process.getInputStream(); // String line; // BufferedReader br = new BufferedReader(new InputStreamReader(in)); // while ((line = br.readLine()) != null) { // System.out.println(line); // } //新啟兩個線程 new DealProcessSream(process.getInputStream()).start(); new DealProcessSream(process.getErrorStream()).start(); process.waitFor(); } catch (Exception e) { } finally { process.destroy(); } } }
public class DealProcessSream extends Thread { private InputStream inputStream; public DealProcessSream(InputStream inputStream) { this.inputStream = inputStream; } public void run() { InputStreamReader inputStreamReader = null; BufferedReader br = null; try { inputStreamReader = new InputStreamReader( inputStream); br = new BufferedReader(inputStreamReader); // 打印信息 // String line = null; // while ((line = br.readLine()) != null) { // System.out.println(line); // } // 不打印信息 while (br.readLine() != null); } catch (IOException ioe) { ioe.printStackTrace(); }finally { try { br.close(); inputStreamReader.close(); } catch (IOException e) { e.printStackTrace(); } } } }
有時需要在程序中調用可執行程序或腳本命令:
Process process = Runtime.getRuntime().exec(shPath); int exitCode = process .waitFor();
Runtime.getRuntime()返回當前應用程序的Runtime對象,該對象的exec()方法指示Java虛擬機創建一個子進程執行指定的可執行程序,
并返回與該子進程對應的Process對象實例。通過Process可以控制該子進程的執行或獲取該子進程的信息。
它的所有標準io(即stdin,stdout,stderr)操作都將通過三個流(getOutputStream(),getInputStream(),getErrorStream())重定向到父進程。
父進程使用這些流來提供到子進程的輸入和獲得從子進程的輸出。因為有些本機平臺僅針對標準輸入和輸出流提供有限的緩沖區大小,如果讀
寫子進程的輸出流或輸入流出現失敗,則可能導致子進程阻塞,甚至產生死鎖。(如果程序不斷在向標準輸出流和標準錯誤流寫數據,而JVM不讀取的話,當緩沖區滿之后將無法繼續寫入數據,最終造成阻塞在waifor()這里。)
process .getErrorStream()
:獲得子進程的錯誤輸出流
process .getInputStream()
:獲得子進程的普通輸出流
簡單示例:
Process shellProcess = null; try { shellProcess = Runtime.getRuntime().exec(shPath); shellErrorResultReader = new BufferedReader(new InputStreamReader(shellProcess.getErrorStream())); shellInfoResultReader = new BufferedReader(new InputStreamReader(shellProcess.getInputStream())); String infoLine; while ((infoLine = shellInfoResultReader.readLine()) != null) { logger.info("腳本文件執行信息:{}", infoLine); } String errorLine; while ((errorLine = shellErrorResultReader.readLine()) != null) { logger.warn("腳本文件執行信息:{}", errorLine); } // 等待程序執行結束并輸出狀態 exitCode = shellProcess.waitFor(); if (0 == exitCode) { logger.info("腳本文件執行成功:" + exitCode); } else { logger.error("腳本文件執行失敗:" + exitCode); } } catch (Exception e) { logger.error("shell腳本執行錯誤", e); } finally { if (null != shellInfoResultReader) { try { shellInfoResultReader.close(); } catch (IOException e) { logger.error("流文件關閉異常:", e); } } if (null != shellErrorResultReader) { try { shellErrorResultReader.close(); } catch (IOException e) { logger.error("流文件關閉異常:", e); } } if (null != shellProcess) { shellProcess.destroy(); } }
關于“ 如何解決java調用process線程阻塞的問題”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。