摘要:類提供了執(zhí)行從進(jìn)程輸入執(zhí)行輸出到進(jìn)程等待進(jìn)程完成檢查進(jìn)程的退出狀態(tài)以及銷毀殺掉進(jìn)程的方法。解決進(jìn)程無限阻塞的方法是在執(zhí)行命令時(shí),設(shè)置一個(gè)超時(shí)時(shí)間,下面提供一個(gè)工具類,對(duì)使用進(jìn)行包裝,向外提供設(shè)置超時(shí)的接口。
在前一篇博文中,簡(jiǎn)單介紹了如何使用Process類來調(diào)用命令行的功能,那樣使用Process會(huì)有一個(gè)很大的問題,就是可能會(huì)出現(xiàn)無限阻塞的情況,永遠(yuǎn)都無法返回結(jié)果。以下是Process的API說明,注意加粗的部分。
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)生死鎖。
解決進(jìn)程無限阻塞的方法是在執(zhí)行命令時(shí),設(shè)置一個(gè)超時(shí)時(shí)間,下面提供一個(gè)工具類,對(duì)Process使用進(jìn)行包裝,向外提供設(shè)置超時(shí)的接口。
ExecuteResult類,對(duì)執(zhí)行命令的結(jié)果進(jìn)行封裝,可以從中獲取退出碼和輸出內(nèi)容。
public class ExecuteResult { @Override public String toString() { return "ExecuteResult [exitCode=" + exitCode + ", executeOut=" + executeOut + "]"; } private int exitCode; private String executeOut; public ExecuteResult(int exitCode, String executeOut) { super(); this.exitCode = exitCode; this.executeOut = executeOut; } public int getExitCode() { return exitCode; } public void setExitCode(int exitCode) { this.exitCode = exitCode; } public String getExecuteOut() { return executeOut; } public void setExecuteOut(String executeOut) { this.executeOut = executeOut; } }
LocalCommandExecutorService 接口,向外暴露executeCommand()方法
public interface LocalCommandExecutorService { ExecuteResult executeCommand(String[] command, long timeout); }
LocalCommandExecutorServiceImpl 實(shí)現(xiàn)類,實(shí)現(xiàn)LocalCommandExecutorService 接口的方法
public class LocalCommandExecutorServiceImpl implements LocalCommandExecutorService { static final Logger logger = LoggerFactory .getLogger(LocalCommandExecutorServiceImpl.class); static ExecutorService pool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 3L, TimeUnit.SECONDS, new SynchronousQueue()); @Override public ExecuteResult executeCommand(String[] command, long timeout) { Process process = null; InputStream pIn = null; InputStream pErr = null; StreamGobbler outputGobbler = null; StreamGobbler errorGobbler = null; Future executeFuture = null; try { process = Runtime.getRuntime().exec(command); final Process p = process; //close process"s output stream. p.getOutputStream().close(); pIn = process.getInputStream(); outputGobbler = new StreamGobbler( pIn, "OUTPUT"); outputGobbler.start(); pErr = process.getErrorStream(); errorGobbler = new StreamGobbler(pErr, "ERROR"); errorGobbler.start(); // create a Callable for the command"s Process which can be called // by an Executor Callable call = new Callable () { public Integer call() throws Exception { p.waitFor(); return p.exitValue(); } }; // submit the command"s call and get the result from a executeFuture = pool.submit(call); int exitCode = executeFuture.get(timeout, TimeUnit.MILLISECONDS); return new ExecuteResult(exitCode, outputGobbler.getContent()); } catch (IOException ex) { String errorMessage = "The command [" + command + "] execute failed."; logger.error(errorMessage, ex); return new ExecuteResult(-1, null); } catch (TimeoutException ex) { String errorMessage = "The command [" + command + "] timed out."; logger.error(errorMessage, ex); return new ExecuteResult(-1, null); } catch (ExecutionException ex) { String errorMessage = "The command [" + command + "] did not complete due to an execution error."; logger.error(errorMessage, ex); return new ExecuteResult(-1, null); } catch (InterruptedException ex) { String errorMessage = "The command [" + command + "] did not complete due to an interrupted error."; logger.error(errorMessage, ex); return new ExecuteResult(-1, null); } finally { if(executeFuture != null){ try{ executeFuture.cancel(true); } catch(Exception ignore){} } if(pIn != null) { this.closeQuietly(pIn); if(outputGobbler != null && !outputGobbler.isInterrupted()){ outputGobbler.interrupt(); } } if(pErr != null) { this.closeQuietly(pErr); if(errorGobbler != null && !errorGobbler.isInterrupted()){ errorGobbler.interrupt(); } } if (process != null) { process.destroy(); } } } private void closeQuietly(Closeable c) { try { if (c != null) c.close(); } catch (IOException e) { } } }
StreamGobbler類,用來包裝輸入輸出流
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class StreamGobbler extends Thread { private static Logger logger = LoggerFactory.getLogger(StreamGobbler.class); private InputStream inputStream; private String streamType; private StringBuilder buf; private volatile boolean isStopped = false; /** * Constructor. * * @param inputStream * the InputStream to be consumed * @param streamType * the stream type (should be OUTPUT or ERROR) * @param displayStreamOutput * whether or not to display the output of the stream being * consumed */ public StreamGobbler(final InputStream inputStream, final String streamType) { this.inputStream = inputStream; this.streamType = streamType; this.buf = new StringBuilder(); this.isStopped = false; } /** * Consumes the output from the input stream and displays the lines * consumed if configured to do so. */ @Override public void run() { try { //默認(rèn)編碼為UTF-8,這里設(shè)置編碼為GBK,因?yàn)閃IN7的編碼為GBK InputStreamReader inputStreamReader = new InputStreamReader( inputStream,"GBK"); BufferedReader bufferedReader = new BufferedReader( inputStreamReader); String line = null; while ((line = bufferedReader.readLine()) != null) { this.buf.append(line + " "); } } catch (IOException ex) { logger.trace("Failed to successfully consume and display the input stream of type " + streamType + ".", ex); } finally { this.isStopped = true; synchronized (this) { notify(); } } } public String getContent() { if(!this.isStopped){ synchronized (this) { try { wait(); } catch (InterruptedException ignore) { } } } return this.buf.toString(); } }
測(cè)試用例
public class LocalCommandExecutorTest { public static void main(String[] args) { LocalCommandExecutorService service = new LocalCommandExecutorServiceImpl(); String[] command = new String[]{"ping","127.0.0.1"}; ExecuteResult result = service.executeCommand(command, 5000); System.out.println("退出碼:"+result.getExitCode()); System.out.println("輸出內(nèi)容:"+result.getExecuteOut()); } }
輸出結(jié)果如下:
直接在命令行執(zhí)行“ping 127.0.0.1”,結(jié)果如下:
Apache提供了一個(gè)開源庫(kù),對(duì)Process類進(jìn)行了封裝,也提供了設(shè)置超時(shí)的功能,建議在項(xiàng)目中使用Apache Commons Exec這個(gè)開源庫(kù)來實(shí)現(xiàn)超時(shí)功能,除了功能更強(qiáng)大外,穩(wěn)定性也有保障。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/64019.html
摘要:如果一個(gè)即時(shí)定時(shí)器是被一個(gè)正在執(zhí)行的回調(diào)排入隊(duì)列的,則該定時(shí)器直到下一次事件循環(huán)迭代才會(huì)被觸發(fā)。參數(shù)描述在事件循環(huán)的當(dāng)前回合結(jié)束時(shí)要調(diào)用的函數(shù)。事件輪詢隨后的調(diào)用,會(huì)在任何事件包括定時(shí)器之前運(yùn)行。 系列文章 Nodejs高性能原理(上) --- 異步非阻塞事件驅(qū)動(dòng)模型Nodejs高性能原理(下) --- 事件循環(huán)詳解 前言 終于開始我nodejs的博客生涯了,先從基本的原理講起.以前寫...
摘要:在之前,都是由類處來實(shí)現(xiàn)進(jìn)程的控制管理。導(dǎo)致當(dāng)前線程等待,如有必要,一直要等到由該對(duì)象表示的進(jìn)程已經(jīng)終止。如果已終止該子進(jìn)程,此方法立即返回。為了防止進(jìn)程無限阻塞或者死鎖,使用類時(shí),需要加上超時(shí)控制,詳細(xì)內(nèi)容可以看博文工具類,提供設(shè)置功能。 ProcessBuilder類是J2SE 1.5在java.lang中新添加的一個(gè)新類,此類用于創(chuàng)建操作系統(tǒng)進(jìn)程,它提供一種啟動(dòng)和管理進(jìn)程(也就是...
摘要:層也就是網(wǎng)絡(luò)傳輸層,在遠(yuǎn)程通信中必然會(huì)涉及到傳輸。值為,不等待消息發(fā)出,將消息放入隊(duì)列,即刻返回。三該類繼承了并且實(shí)現(xiàn)接口,是服務(wù)器抽象類。八該類是多消息處理器的抽象類。創(chuàng)建線程池設(shè)置組件的獲得實(shí)例把線程池放到 遠(yuǎn)程通訊——Transport層 目標(biāo):介紹Transport層的相關(guān)設(shè)計(jì)和邏輯、介紹dubbo-remoting-api中的transport包內(nèi)的源碼解析。 前言 先預(yù)警一...
摘要:還有,需要支持字符編碼設(shè)置,在下對(duì)象調(diào)試程序很有幫助,因此,我們可以列表表示整個(gè)需求。第二種是無法設(shè)置字符編碼的,而第一種是獲得了整個(gè)標(biāo)準(zhǔn)輸出和錯(cuò)誤輸出后再設(shè)置字符編碼的。 概述 寫這篇的主要目的是為了整理和記錄,歸檔以便以后查閱。 我之前在SF上提問了一個(gè)問題:如何正確使用PipedInputStream和PipedOutputStream 問題中提到的Apache Commons ...
摘要:新加了一個(gè)微任務(wù)和一個(gè)宏任務(wù)在當(dāng)前執(zhí)行棧的尾部下一次之前觸發(fā)回調(diào)函數(shù)。階段這個(gè)階段主要執(zhí)行一些系統(tǒng)操作帶來的回調(diào)函數(shù),如錯(cuò)誤,如果嘗試鏈接時(shí)出現(xiàn)錯(cuò)誤,一些會(huì)把這個(gè)錯(cuò)誤報(bào)告給。 JavaScript引擎又稱為JavaScript解釋器,是JavaScript解釋為機(jī)器碼的工具,分別運(yùn)行在瀏覽器和Node中。而根據(jù)上下文的不同,Event loop也有不同的實(shí)現(xiàn):其中Node使用了libu...
閱讀 732·2021-11-24 10:30
閱讀 1267·2021-09-24 09:48
閱讀 3083·2021-09-24 09:47
閱讀 3602·2019-08-29 17:11
閱讀 2885·2019-08-29 15:38
閱讀 2280·2019-08-29 11:03
閱讀 3607·2019-08-26 12:15
閱讀 1018·2019-08-26 10:45