java使用Process調(diào)用exe程序及Process.waitFor()死鎖問(wèn)題解決
前言
最近在開(kāi)發(fā)android的同時(shí)也在開(kāi)發(fā)java ,碰到了需要使用java 程序調(diào)用exe的需求,這里我使用的 process 來(lái)調(diào)用的。該篇文章 讀完需要8+分鐘,文章類(lèi)型為 小白入門(mén)類(lèi)型,此處主要記錄,方便以后學(xué)習(xí)補(bǔ)充… 如有不正確的地方還望海涵 及 指出….
文章參考
1. 使用process調(diào)用exe程序
ProcessBuilder pb = new ProcessBuilder("C:\\Debug\\TestRedis.exe", keyNmae); pb.redirectErrorStream(true); Process process = pb.start(); //可能導(dǎo)致進(jìn)程阻塞,甚至死鎖 int ret = process.waitFor(); System.out.println("return value:"+ret); System.out.println(process.exitValue()); byte[] bytes = new byte[process.getInputStream().available()]; process.getInputStream().read(bytes); System.out.println(new String(bytes));
// ProcessBuilder api 方法 public ProcessBuilder(String... command) { this.command = new ArrayList<>(command.length); for (String arg : command) this.command.add(arg); }
首先我們先使用 processBuilder 創(chuàng)建出該對(duì)象,該對(duì)象我這里暫時(shí)使用了第一個(gè)參數(shù)為 exe 文件的地址,第二個(gè)參數(shù)為傳遞參數(shù),是我需要傳給exe 的字符串。后邊主要就是打印 輸入流,獲取exe 輸出信息。其實(shí)到這里java 調(diào)用exe 就已經(jīng)完 了,但是后續(xù)開(kāi)發(fā)中遇到一種問(wèn)題,就是程序莫名死鎖,沒(méi)有響應(yīng),于是使用debug 跟進(jìn)代碼,發(fā)現(xiàn)程序走到 waitfor 代碼行的時(shí)候程序就出現(xiàn)了掛起的情況,于是google了一番,明白了其中的原因。
2. waitfor 問(wèn)題描述分析
1.主進(jìn)程中調(diào)用pb.start會(huì)創(chuàng)建一個(gè)子進(jìn)程,用于執(zhí)行shell /exe 腳本。子進(jìn)程創(chuàng)建后會(huì)和主進(jìn)程分別獨(dú)立運(yùn)行。
2. 因?yàn)橹鬟M(jìn)程需要等待腳本執(zhí)行完成,然后對(duì)腳本返回值或輸出進(jìn)行處理,所以這里主進(jìn)程調(diào)用Process.waitfor等待子進(jìn)程完成。
3. 子進(jìn)程執(zhí)行過(guò)程就是不斷的打印信息。主進(jìn)程中可以通過(guò)Process.getInputStream和Process.getErrorStream獲取并處理。
4. 這時(shí)候子進(jìn)程不斷向主進(jìn)程發(fā)生數(shù)據(jù),而主進(jìn)程調(diào)用Process.waitfor后已掛起。當(dāng)前子進(jìn)程和主進(jìn)程之間的緩沖區(qū)塞滿后,子進(jìn)程不能繼續(xù)寫(xiě)數(shù)據(jù),然后也會(huì)掛起。
5. 這樣子進(jìn)程等待主進(jìn)程讀取數(shù)據(jù),主進(jìn)程等待子進(jìn)程結(jié)束,兩個(gè)進(jìn)程相互等待,最終導(dǎo)致死鎖。
3. 死鎖問(wèn)題解決
基于上述分析,只要主進(jìn)程在waitfor之前,能不斷處理緩沖區(qū)中的數(shù)據(jù)就可以。因?yàn)?,我們可以再waitfor之前,單獨(dú)啟兩個(gè)額外的線程,分別用于處理InputStream和ErrorStream就可以
try { //獲取進(jìn)程的標(biāo)準(zhǔn)輸入流 final InputStream is1 = process.getInputStream(); //獲取進(jìn)城的錯(cuò)誤流 final InputStream is2 = process.getErrorStream(); //啟動(dòng)兩個(gè)線程,一個(gè)線程負(fù)責(zé)讀標(biāo)準(zhǔn)輸出流,另一個(gè)負(fù)責(zé)讀標(biāo)準(zhǔn)錯(cuò)誤流 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(); //可能導(dǎo)致進(jìn)程阻塞,甚至死鎖 int ret = process.waitFor(); System.out.println("return value:"+ret); System.out.println(process.exitValue()); logger.info("event:{}", "RunExeForWindows",process.exitValue()); byte[] bytes = new byte[process.getInputStream().available()]; process.getInputStream().read(bytes); System.out.println(new String(bytes)); logger.info("event:{}", "RunExeForWindows",new String(bytes)); }catch (Exception ex){ ex.printStackTrace(); try{ process.getErrorStream().close(); process.getInputStream().close(); process.getOutputStream().close(); } catch(Exception ee){} }
如此便可以將 waitfor死鎖問(wèn)題避開(kāi),看完這個(gè)問(wèn)題,總結(jié)一下,多看官方api注釋….其實(shí)官方已經(jīng)提示我們,如下 為 api注釋
Causes the current thread to wait, if necessary, until the * process represented by this {@code Process} object has * terminated. This method returns immediately if the subprocess * has already terminated. If the subprocess has not yet * terminated, the calling thread will be blocked until the * subprocess exits. @return the exit value of the subprocess represented by this * {@code Process} object. By convention, the value * {@code 0} indicates normal termination. * @throws InterruptedException if the current thread is * {@linkplain Thread#interrupt() interrupted} by another * thread while it is waiting, then the wait is ended and * an {@link InterruptedException} is thrown.
如果需要,導(dǎo)致當(dāng)前線程等待,直到此{(lán)@code Process}對(duì)象表示的進(jìn)程具有終止 如果子進(jìn)程,此方法立即返回已經(jīng)終止。 如果子進(jìn)程還沒(méi)有終止后,調(diào)用線程將被阻塞,直到子進(jìn)程退出。
總結(jié)
到此這篇關(guān)于java使用Process調(diào)用exe程序及Process.waitFor()死鎖問(wèn)題解決的文章就介紹到這了,更多相關(guān)java用Process調(diào)用exe程序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot跨域CORS處理實(shí)現(xiàn)原理
這篇文章主要介紹了Springboot跨域CORS處理實(shí)現(xiàn)原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04spring的構(gòu)造函數(shù)注入屬性@ConstructorBinding用法
這篇文章主要介紹了關(guān)于spring的構(gòu)造函數(shù)注入屬性@ConstructorBinding用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12Spring AOP的幾種實(shí)現(xiàn)方式總結(jié)
本篇文章主要介紹了Spring AOP的幾種實(shí)現(xiàn)方式總結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02SpringBoot項(xiàng)目中出現(xiàn)不同端口跨域問(wèn)題的解決方法
這篇文章主要介紹了SpringBoot項(xiàng)目中出現(xiàn)不同端口跨域問(wèn)題的解決方法,文中介紹了兩種解決方法,并給出了詳細(xì)的代碼供大家參考,具有一定的參考價(jià)值,需要的朋友可以參考下2024-03-03

SpringBoot結(jié)合Redis配置工具類(lèi)實(shí)現(xiàn)動(dòng)態(tài)切換庫(kù)

java Hibernate save()與persist()區(qū)別

解決Eclipse配置Tomcat出現(xiàn)Cannot create a server using the selected

IntelliJ中高效重構(gòu)的10個(gè)快捷方式詳解