摘要:類提供了執行從進程輸入執行輸出到進程等待進程完成檢查進程的退出狀態以及銷毀殺掉進程的方法。解決進程無限阻塞的方法是在執行命令時,設置一個超時時間,下面提供一個工具類,對使用進行包裝,向外提供設置超時的接口。
在前一篇博文中,簡單介紹了如何使用Process類來調用命令行的功能,那樣使用Process會有一個很大的問題,就是可能會出現無限阻塞的情況,永遠都無法返回結果。以下是Process的API說明,注意加粗的部分。
ProcessBuilder.start() 和 Runtime.exec 方法創建一個本機進程,并返回 Process 子類的一個實例,該實例可用來控制進程并獲得相關信息。Process 類提供了執行從進程輸入、執行輸出到進程、等待進程完成、檢查進程的退出狀態以及銷毀(殺掉)進程的方法。
創建進程的方法可能無法針對某些本機平臺上的特定進程很好地工作,比如,本機窗口進程,守護進程,Microsoft Windows 上的 Win16/DOS 進程,或者 shell 腳本。創建的子進程沒有自己的終端或控制臺。它的所有標準 io(即 stdin、stdout 和 stderr)操作都將通過三個流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父進程。父進程使用這些流來提供到子進程的輸入和獲得從子進程的輸出。因為有些本機平臺僅針對標準輸入和輸出流提供有限的緩沖區大小,如果讀寫子進程的輸出流或輸入流迅速出現失敗,則可能導致子進程阻塞,甚至產生死鎖。
解決進程無限阻塞的方法是在執行命令時,設置一個超時時間,下面提供一個工具類,對Process使用進行包裝,向外提供設置超時的接口。
ExecuteResult類,對執行命令的結果進行封裝,可以從中獲取退出碼和輸出內容。
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 實現類,實現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 { //默認編碼為UTF-8,這里設置編碼為GBK,因為WIN7的編碼為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(); } }
測試用例
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("輸出內容:"+result.getExecuteOut()); } }
輸出結果如下:
直接在命令行執行“ping 127.0.0.1”,結果如下:
Apache提供了一個開源庫,對Process類進行了封裝,也提供了設置超時的功能,建議在項目中使用Apache Commons Exec這個開源庫來實現超時功能,除了功能更強大外,穩定性也有保障。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64019.html
摘要:如果一個即時定時器是被一個正在執行的回調排入隊列的,則該定時器直到下一次事件循環迭代才會被觸發。參數描述在事件循環的當前回合結束時要調用的函數。事件輪詢隨后的調用,會在任何事件包括定時器之前運行。 系列文章 Nodejs高性能原理(上) --- 異步非阻塞事件驅動模型Nodejs高性能原理(下) --- 事件循環詳解 前言 終于開始我nodejs的博客生涯了,先從基本的原理講起.以前寫...
摘要:在之前,都是由類處來實現進程的控制管理。導致當前線程等待,如有必要,一直要等到由該對象表示的進程已經終止。如果已終止該子進程,此方法立即返回。為了防止進程無限阻塞或者死鎖,使用類時,需要加上超時控制,詳細內容可以看博文工具類,提供設置功能。 ProcessBuilder類是J2SE 1.5在java.lang中新添加的一個新類,此類用于創建操作系統進程,它提供一種啟動和管理進程(也就是...
摘要:層也就是網絡傳輸層,在遠程通信中必然會涉及到傳輸。值為,不等待消息發出,將消息放入隊列,即刻返回。三該類繼承了并且實現接口,是服務器抽象類。八該類是多消息處理器的抽象類。創建線程池設置組件的獲得實例把線程池放到 遠程通訊——Transport層 目標:介紹Transport層的相關設計和邏輯、介紹dubbo-remoting-api中的transport包內的源碼解析。 前言 先預警一...
摘要:還有,需要支持字符編碼設置,在下對象調試程序很有幫助,因此,我們可以列表表示整個需求。第二種是無法設置字符編碼的,而第一種是獲得了整個標準輸出和錯誤輸出后再設置字符編碼的。 概述 寫這篇的主要目的是為了整理和記錄,歸檔以便以后查閱。 我之前在SF上提問了一個問題:如何正確使用PipedInputStream和PipedOutputStream 問題中提到的Apache Commons ...
摘要:新加了一個微任務和一個宏任務在當前執行棧的尾部下一次之前觸發回調函數。階段這個階段主要執行一些系統操作帶來的回調函數,如錯誤,如果嘗試鏈接時出現錯誤,一些會把這個錯誤報告給。 JavaScript引擎又稱為JavaScript解釋器,是JavaScript解釋為機器碼的工具,分別運行在瀏覽器和Node中。而根據上下文的不同,Event loop也有不同的實現:其中Node使用了libu...
閱讀 720·2021-11-24 10:30
閱讀 1255·2021-09-24 09:48
閱讀 3074·2021-09-24 09:47
閱讀 3588·2019-08-29 17:11
閱讀 2875·2019-08-29 15:38
閱讀 2270·2019-08-29 11:03
閱讀 3594·2019-08-26 12:15
閱讀 1008·2019-08-26 10:45