轉自: http://berdy.iteye.com/blog/810223
Runtime 封裝着java程序的運行時環境。通過Runtime實例,java應用能夠與其運行的環境連接。Runtime在jvm中保持一個單例,所以不能通過Runtime類的構造函數。只能通過Runtime.getRuntime()來獲的當前Runtime的一個實例。獲得Runtime實例后,就可以通過Runtime的exec()方法在當前jvm進程外啟動其他進程了。很常見的一個應用就是,啟動瀏覽器進程來顯示一個程序的幫助頁面。
在Runtime類中存在四個exec()重載方法.
- public Process exec(String command);
- public Process exec(String [] cmdArray);
- public Process exec(String command, String [] envp);
- public Process exec(String [] cmdArray, String [] envp);
主要參數是要啟動進程的名稱,以及啟動該進程時需要的參數。然后是一些環境相關的屬性。envp是已name=value,
形式傳入的。具體查看下源碼便一目了然了。
通常,啟動另外一個進程后,需要獲取另外一個進程的執行結果,然后根據結果執行后續的流程。要獲取外部進程的運行結果,可以調用Process的exitValue() 方法。下面代碼中啟動一個java編譯器進程。
- try {
- Runtime rt = Runtime.getRuntime();
- Process proc = rt.exec("javac");
- int exitVal = proc.exitValue();
- System.out.println("Process exitValue: " + exitVal);
- } catch (Throwable t) {
- t.printStackTrace();
- }
不幸的是,你將看到下面的結果:
java.lang.IllegalThreadStateException: process has not exited
at java.lang.ProcessImpl.exitValue(Native Method)
at com.test.runtime.Test.BadExecJavac(Test.java:13)
at com.test.runtime.Test.main(Test.java:5)
原因是exitValue()方法並不會等待外部進程結束。如果外部進程還未結束,exitValue()將會拋出IllegalThreadStateException。解決辦法就是調用Process的waitfor()方法。waitfor()方法會掛起當前線程,一直等到外部進程結束。當然使用exitValue()或者waitfor()完全取決你的需求。可以設個boolean標志,來確定使用哪個。運行下面的代碼:
- try {
- Runtime rt = Runtime.getRuntime();
- Process proc = rt.exec("javac");
- int exitVal = proc.waitFor();
- System.out.println("Process exitValue: " + exitVal);
- } catch (Throwable t) {
- t.printStackTrace();
- }
發現程序被阻塞了,什么原因呢?JDK's Javadoc文檔解釋說:
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.
翻譯:
一些平台只為標准輸入輸出提供有限的緩存。錯誤的寫子進程的輸入流或者錯誤的都子進程的輸出流都有可能造成子進程的阻塞,甚至是死鎖。
解決上面問題的辦法就是程序中將子進程的輸出流和錯誤流都輸出到標准輸出中。
- try {
- Runtime rt = Runtime.getRuntime();
- Process proc = rt.exec("javac");
- InputStream stderr = proc.getErrorStream();
- InputStreamReader isr = new InputStreamReader(stderr);
- BufferedReader br = new BufferedReader(isr);
- String line = null;
- System.out.println("<ERROR>");
- while ((line = br.readLine()) != null)
- System.out.println(line);
- System.out.println("</ERROR>");
- int exitVal = proc.waitFor();
- System.out.println("Process exitValue: " + exitVal);
- } catch (Throwable t) {
- t.printStackTrace();
- }
上面的代碼中僅僅是輸出了錯誤流,並沒有輸出子進程的輸出流。在程序中最好是能將子進程的錯誤流和輸出流都能輸出並清空。
在windows系統中,很多人會利用Runtime.exec()來調用不可執行的命令。例如dir和copy;
- try {
- Runtime rt = Runtime.getRuntime();
- Process proc = rt.exec("dir");
- InputStream stdin = proc.getInputStream();
- InputStreamReader isr = new InputStreamReader(stdin);
- BufferedReader br = new BufferedReader(isr);
- String line = null;
- System.out.println("<OUTPUT>");
- while ((line = br.readLine()) != null)
- System.out.println(line);
- System.out.println("</OUTPUT>");
- int exitVal = proc.waitFor();
- System.out.println("Process exitValue: " + exitVal);
- } catch (Throwable t) {
- t.printStackTrace();
- }
運行上面的代碼,將會得到一個錯誤代碼為2的錯誤。在win32系統中,error=2表示文件未找到。也就是不存在dir.exe和copy.exe。這是因為dir命令式windows中命令行解析器的一部分,並不是單獨的一個可執行的命令。要運行上面的命令,得先啟動windows下的命令行解析器command.com或者cmd.exe,這個取決於windows的系統的版本。
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- class StreamGobbler extends Thread {
- InputStream is;
- String type;
- StreamGobbler(InputStream is, String type) {
- this.is = is;
- this.type = type;
- }
- public void run() {
- try {
- InputStreamReader isr = new InputStreamReader(is);
- BufferedReader br = new BufferedReader(isr);
- String line = null;
- while ((line = br.readLine()) != null)
- System.out.println(type + ">" + line);
- } catch (IOException ioe) {
- ioe.printStackTrace();
- }
- }
- }
- public class GoodWindowsExec {
- public static void main(String args[]) {
- if (args.length < 1) {
- System.out.println("USAGE: java GoodWindowsExec <cmd>");
- System.exit(1);
- }
- try {
- String osName = System.getProperty("os.name");
- String[] cmd = new String[3];
- if (osName.equals("Windows NT")) {
- cmd[0] = "cmd.exe";
- cmd[1] = "/C";
- cmd[2] = args[0];
- } else if (osName.equals("Windows 95")) {
- cmd[0] = "command.com";
- cmd[1] = "/C";
- cmd[2] = args[0];
- }
- Runtime rt = Runtime.getRuntime();
- System.out.println("Execing " + cmd[0] + " " + cmd[1] + " " + cmd[2]);
- Process proc = rt.exec(cmd);
- StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");
- StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");
- errorGobbler.start();
- outputGobbler.start();
- int exitVal = proc.waitFor();
- System.out.println("ExitValue: " + exitVal);
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
- }
另外,Runtime.exec()並不是命令解析器,這是啟動某個進程。並不能執行一些命令行的命令。下面是一個常見的錯誤:
- try {
- Runtime rt = Runtime.getRuntime();
- Process proc = rt.exec("java jecho 'Hello World' > test.txt");
- StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");
- StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");
- errorGobbler.start();
- outputGobbler.start();
- int exitVal = proc.waitFor();
- System.out.println("ExitValue: " + exitVal);
- } catch (Throwable t) {
- t.printStackTrace();
- }
上面的代碼希望像DOS系統中一樣將命令的執行結果輸出到文件中去。但是Runtime.exec()並不是命令行解析器。要想重定向輸出流,必須在程序中編碼實現。
- import java.util.*;
- import java.io.*;
- class StreamGobbler extends Thread {
- InputStream is;
- String type;
- OutputStream os;
- StreamGobbler(InputStream is, String type) {
- this(is, type, null);
- }
- StreamGobbler(InputStream is, String type, OutputStream redirect) {
- this.is = is;
- this.type = type;
- this.os = redirect;
- }
- public void run() {
- try {
- PrintWriter pw = null;
- if (os != null)
- pw = new PrintWriter(os);
- InputStreamReader isr = new InputStreamReader(is);
- BufferedReader br = new BufferedReader(isr);
- String line = null;
- while ((line = br.readLine()) != null) {
- if (pw != null)
- pw.println(line);
- System.out.println(type + ">" + line);
- }
- if (pw != null)
- pw.flush();
- } catch (IOException ioe) {
- ioe.printStackTrace();
- }
- }
- }
- public class GoodWinRedirect {
- public static void main(String args[]) {
- if (args.length < 1) {
- System.out.println("USAGE java GoodWinRedirect <outputfile>");
- System.exit(1);
- }
- try {
- FileOutputStream fos = new FileOutputStream(args[0]);
- Runtime rt = Runtime.getRuntime();
- Process proc = rt.exec("java jecho 'Hello World'");
- StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");
- StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT", fos);
- errorGobbler.start();
- outputGobbler.start();
- int exitVal = proc.waitFor();
- System.out.println("ExitValue: " + exitVal);
- fos.flush();
- fos.close();
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
- }