您好,登錄后才能下訂單哦!
這篇文章主要介紹了如何解決Process.getInputStream()阻塞的問題,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
Java中
Runtime.getInstance().exec (String cmd)
或者
new ProcessBuilder(String cmd).start()
都可以產(chǎn)生子進(jìn)程對(duì)象Process。通過調(diào)用Process對(duì)象的waitFor()方法可以使主進(jìn)程進(jìn)入等待狀態(tài),直至子進(jìn)程執(zhí)行完畢,再進(jìn)行下一步工作。如果對(duì)子進(jìn)程處理不當(dāng),有可能造成主進(jìn)程阻塞,整個(gè)程序死掉。
ProcessBuilder.start() 和 Runtime.exec 方法創(chuàng)建一個(gè)本機(jī)進(jìn)程,并返回 Process 子類的一個(gè)實(shí)例,該實(shí)例可用來控制進(jìn)程并獲取相關(guān)信息。Process 類提供了執(zhí)行從進(jìn)程輸入、執(zhí)行輸出到進(jìn)程、等待進(jìn)程完成、檢查進(jìn)程的退出狀態(tài)以及銷毀(殺掉)進(jìn)程的方法。
創(chuàng)建進(jìn)程的方法可能無法針對(duì)某些本機(jī)平臺(tái)上的特定進(jìn)程很好地工作,比如,本機(jī)窗口進(jìn)程,守護(hù)進(jìn)程,Microsoft Windows 上的 Win16/DOS 進(jìn)程,或者 shell 腳本。創(chuàng)建的子進(jìn)程沒有自己的終端或控制臺(tái)。它的所有標(biāo)準(zhǔn) io(即 stdin,stdout,stderr)操作都將通過三個(gè)流 (getOutputStream(),getInputStream(),getErrorStream()) 重定向到父進(jìn)程。父進(jìn)程使用這些流來提供到子進(jìn)程的輸入和獲得從子進(jìn)程的輸出。因?yàn)橛行┍緳C(jī)平臺(tái)僅針對(duì)標(biāo)準(zhǔn)輸入和輸出流提供有限的緩沖區(qū)大小,如果讀寫子進(jìn)程的輸出流或輸入流迅速出現(xiàn)失敗,則可能導(dǎo)致子進(jìn)程阻塞,甚至產(chǎn)生死鎖。
在對(duì)getOutputStream(),getInputStream(),getErrorStream()的描述中,有個(gè)注意事項(xiàng):對(duì)其輸出流和錯(cuò)誤流進(jìn)行緩沖是一個(gè)好主意!嗯,好抽象??!
問題正在于此,Process.getInputStream()和Process.getErrorStream()分別返回Process的標(biāo)準(zhǔn)輸出流和錯(cuò)誤流,兩個(gè)流如果處理不當(dāng),其緩沖區(qū)不能被及時(shí)清除而被塞滿,則進(jìn)程被阻塞,即使調(diào)用Process.destory()也未必能銷毀被阻塞的子進(jìn)程。
如果嘗試同步獲取Process的輸出流和錯(cuò)誤流進(jìn)行處理,未必有效,順序執(zhí)行過程中,輸出流和錯(cuò)誤流常常不能得到及時(shí)處理。解決方案有兩個(gè)。
通過啟動(dòng)兩個(gè)線程來并發(fā)地讀取和處理輸出流和錯(cuò)誤流,懶得打開IDE了,就大概敲一下代碼吧,可能有錯(cuò)誤,如下:
調(diào)用者:
class ProcessExecutor { private Process p; private List<String> outputList; private List<String> errorOutputList; public ProcessExecutor(Process p) throws IOException { if(null == p) { throw new IOException("the provided Process is null"); } this. p = p; } public List<String> getOutputList() { return this. outputList; } public List<String> getErrorOutputList() { return this.errorOutputList; } public int execute() { int rs = 0; Thread outputThread = new ProcessOutputThread(this.p.getInputStream()); Thread errorOutputThread = new ProcessOutputThread(this.p.getErrorStream()); outputThread.start(); errorOutputThread.start(); rs = p.waitFor(); outputThread.join(); errorOutputThread.join(); this.outputList = outputThread.getOutputList(); this.errorOutputList = errorOutputThread.getOutputList(); return rs; } }
流處理線程
class ProcessOutputThread extends Thread { private InputStream is; private List<String> outputList; public ProcessOutputThread(InputStream is) throws IOException { if(null == is) { throw new IOException("the provided InputStream is null"); } this. is = is; this.outputList = new ArrayList<String>(); } public List<String> getOutputList() { return this. outputList; } @Override public void run() { InputStreamReader ir = null; BufferedReader br = null; try { ir = new InputStreamReader(this.is); br = new BufferedReader(ir); String output = null; while(null != (output = br.readLine())) { print(output); this.outputList.add(output); } } catch(IOException e) { e.print(); } finally ( try { if(null != br) { br.close(); } if(null != ir) { ir.close(); } if(null != this.is) { this.is.close(); } } catch(IOException e) { e.print(); } ) } }
public int execute() { int rs = 0; String[] cmds = {...};//command and arg ProcessBuilder builder = new ProcessBuilder(cmds); builder.redirectErrorStream(true); Process process = builder.start(); BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); String output = null; while (null != (readLine = br.readLine())) { print(output); } rs = process.waitFor(); return rs; }
Process阻塞原因:輸入流和錯(cuò)誤流分開的,沒有處理,就會(huì)發(fā)生阻塞,歸根結(jié)底本質(zhì)上是bio引起的io阻塞問題。
getInputStream,getErrorSteam就是獲取腳本或者命令的控制臺(tái)回顯信息,前者獲取的是標(biāo)準(zhǔn)輸出的回顯信息,后者獲取的是標(biāo)準(zhǔn)錯(cuò)誤的回顯信息
Process原理:使用Runtime.getRuntime().exec(cmd)會(huì)在當(dāng)前進(jìn)程建立一個(gè)子進(jìn)程,子進(jìn)程由于沒有控制臺(tái),它的標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤就會(huì)返回給父進(jìn)程Process,因此通過getInputStream和getErrorStream就可以獲取到這些信息。
測試代碼如下:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; public class JavaExeBat { public JavaExeBat() { } public static void main(String[] args) { Process p; //test.bat中的命令是ipconfig/all String cmd="sh test.sh "; //String cmd="ping 127.0.0.1 -c 4"; try { //執(zhí)行命令 p = Runtime.getRuntime().exec(cmd); //取得命令結(jié)果的輸出流 //輸出流 InputStream fis=p.getInputStream(); //錯(cuò)誤流 InputStream ferrs=p.getErrorStream(); //用一個(gè)讀輸出流類去讀 InputStreamReader isr=new InputStreamReader(fis); InputStreamReader errsr=new InputStreamReader(ferrs); //用緩沖器讀行 BufferedReader br=new BufferedReader(isr); BufferedReader errbr=new BufferedReader(errsr); String line=null; String lineerr = null; //直到讀完為止 while((line=br.readLine())!=null) { //有可能發(fā)生阻塞的問題 System.out.println("return input Str:" + line); } while((lineerr=errbr.readLine())!=null){ //有可能發(fā)生阻塞的問題 System.out.println("return err Str:" + lineerr); } int exitVal = p.waitFor(); System.out.println("exitVal:" + exitVal); } catch (Exception e) { e.printStackTrace(); } } }
test.sh如下
#!/bin/bash for((i=0; i < 100000; i++));do //輸出的標(biāo)準(zhǔn)輸出 echo "testaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" //輸出到標(biāo)準(zhǔn)錯(cuò)誤 echo "testaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 1>&2 done
經(jīng)過測試發(fā)現(xiàn),如果JavaExeBat.java文件中只開啟標(biāo)準(zhǔn)輸出或者標(biāo)準(zhǔn)錯(cuò)誤時(shí),進(jìn)程就會(huì)夯住,無法通過waiteFor獲取其返回值,因?yàn)槟_本中分別輸出了100000w條信息到標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤,而下述代碼只處理了getInputStream,導(dǎo)致標(biāo)準(zhǔn)錯(cuò)誤輸出流的信息太多返回給當(dāng)前進(jìn)程,沒有得到處理,因此阻塞。
代碼如下:
p = Runtime.getRuntime().exec(cmd); //取得命令結(jié)果的輸出流 //輸出流 InputStream fis=p.getInputStream(); //用一個(gè)讀輸出流類去讀 InputStreamReader isr=new InputStreamReader(fis); //用緩沖器讀行 BufferedReader br=new BufferedReader(isr); String line=null; //直到讀完為止 while((line=br.readLine())!=null) { //有可能發(fā)生阻塞的問題 System.out.println("return input Str:" + line); } int exitVal = p.waitFor(); System.out.println("exitVal:" + exitVal);
把上述代碼中的getInputStream換做getErrorStream,也會(huì)夯住進(jìn)程,因?yàn)橥瑯又惶幚砹藘烧咧幸徽?,即?biāo)準(zhǔn)錯(cuò)誤。
那么能不能同步處理兩個(gè)流信息呢?代碼如下:
try { //執(zhí)行命令 p = Runtime.getRuntime().exec(cmd); //取得命令結(jié)果的輸出流 //輸出流 InputStream fis=p.getInputStream(); //錯(cuò)誤流 InputStream ferrs=p.getErrorStream(); //用一個(gè)讀輸出流類去讀 InputStreamReader isr=new InputStreamReader(fis); InputStreamReader errsr=new InputStreamReader(ferrs); //用緩沖器讀行 BufferedReader br=new BufferedReader(isr); BufferedReader errbr=new BufferedReader(errsr); String line=null; String lineerr = null; //直到讀完為止 while((line=br.readLine())!=null) { //有可能發(fā)生阻塞的問題 System.out.println("return input Str:" + line); } while((lineerr=errbr.readLine())!=null){ //有可能發(fā)生阻塞的問題 System.out.println("return err Str:" + lineerr); } int exitVal = p.waitFor(); System.out.println("exitVal:" + exitVal); } catch (Exception e) { e.printStackTrace(); } }
測試過后發(fā)現(xiàn)也不行,因?yàn)槭峭降?,就?huì)有先后順序,也會(huì)發(fā)生阻塞,測試方法,將test.sh改為只打印標(biāo)準(zhǔn)錯(cuò)誤,就會(huì)發(fā)現(xiàn)標(biāo)準(zhǔn)錯(cuò)誤處理被阻塞,腳本如下:
#!/bin/bash for((i=0; i < 100000; i++));do //輸出到標(biāo)準(zhǔn)錯(cuò)誤 echo "testaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 1>&2 done
(1)并發(fā)處理兩個(gè)流信息,開啟兩個(gè)線程分別處理輸出流與錯(cuò)誤流
(2)將兩個(gè)流合并為一個(gè)流解決示例:
class ProcessExecutor { private Process p; private List<String> outputList; private List<String> errorOutputList; public ProcessExecutor(Process p) throws IOException { if(null == p) { throw new IOException("the provided Process is null"); } this. p = p; } public List<String> getOutputList() { return this. outputList; } public List<String> getErrorOutputList() { return this.errorOutputList; } public int execute() { int rs = 0; Thread outputThread = new ProcessOutputThread(this.p.getInputStream()); Thread errorOutputThread = new ProcessOutputThread(this.p.getErrorStream()); outputThread.start(); errorOutputThread.start(); rs = p.waitFor(); outputThread.join(); errorOutputThread.join(); this.outputList = outputThread.getOutputList(); this.errorOutputList = errorOutputThread.getOutputList(); return rs; } } class ProcessOutputThread extends Thread { private InputStream is; private List<String> outputList; public ProcessOutputThread(InputStream is) throws IOException { if(null == is) { throw new IOException("the provided InputStream is null"); } this. is = is; this.outputList = new ArrayList<String>(); } public List<String> getOutputList() { return this. outputList; } @Override public void run() { InputStreamReader ir = null; BufferedReader br = null; try { ir = new InputStreamReader(this.is); br = new BufferedReader(ir); String output = null; while(null != (output = br.readLine())) { print(output); this.outputList.add(output); } } catch(IOException e) { e.print(); } finally ( try { if(null != br) { br.close(); } if(null != ir) { ir.close(); } if(null != this.is) { this.is.close(); } } catch(IOException e) { e.print(); } ) } }
public int execute() { int rs = 0; String[] cmds = {...};//command and arg ProcessBuilder builder = new ProcessBuilder(cmds); builder.redirectErrorStream(true); Process process = builder.start(); BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); String output = null; while (null != (readLine = br.readLine())) { print(output); } rs = process.waitFor(); return rs; }
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“如何解決Process.getInputStream()阻塞的問題”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。