程序如下:
import java.util.*;
import java.io.*;
public class BadExecJavac
{
public static void main(String args[])
{
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();
}
}
}
運行結果如下
E:\classes\com\javaworld\jpitfalls\article2>java BadExecJavac
java.lang.IllegalThreadStateException: process has not exited
at java.lang.Win32Process.exitValue(Native Method)
at BadExecJavac.main(BadExecJavac.java:13)這是因為當進程還沒有結束的情況下,調用exitValue方法會拋出IllegalThreadStateException.當然了我們會問為什嗎這個方法不會等到進程結束在返回一個合理的值?
在檢查Process類的所有可用方法以后我們發現WairFor()是一個更合適的方法。事實上waitFor也會返回exit value。這意味著你不可以同時用exitvalue和waitfor,而是只能選擇一個。
當然了也有情況你要在waitfor之前用exitvalue方法:就是你不想因為外部程序永遠無法完成而一直等待下去。
因此為了避免這個陷阱,我們要么捕獲IllegalThreadStateException異常,要么等待進程完成。我們相當然的以為可以用waitfor來等待程序的結束。代碼如下:
import java.util.*;
import java.io.*;
public class BadExecJavac2{
public static void main(String args[]) {
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();
}
}
}這次在linux下面返回的結果是2,而在windows下面據說程序會掛起,關于其原因我們可以在jdk文檔中找到部分解釋:因為一些操作系統為標準的輸入輸出僅僅提供有限的緩沖區,當不能正確的將信息寫進輸入流或者從輸出流中獲取信息時,就會導致子進程的阻塞,甚至死鎖。現在我們就根據jdk文檔來處理javac進程的輸出,當你不帶任何參數運行javac時,它會打印出一系列的有用的提示信息。而這些會被傳送到stderr流中。我們可以寫程序在其返回前獲取這些信息。下面的代碼提供了一個平庸的解決方案。
import java.util.*;import java.io.*;
public class MediocreExecJavac{
public static void main(String args[]) {
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();
}
}
}這次程序可以正確的輸出了提示信息,但是我們應該注意到其返回代碼是2,我們知道任何非0的返回代碼都表示程序不正常。所以我們需要進一步的查找原因。對于win32而言是file not found,很明顯javac期望我們提供編譯的文件。所以對于永遠掛起的問題,如果你運行的程序會有輸出或者要求輸出入時,你需要處理輸出和輸入。我在linux下面運行的結果是正確的。前面說了在win32下面2代表是文件沒有找到,而在這種情況下表明是dir.exe沒有找到,(因為根本就沒有這個文件,他們都被封裝到common.com (win95)或者cmd.exe中了。
下面我們列出一個正確的處理Process的輸入輸出流的方法。需要用一個線程類。
import java.util.*;
import java.io.*;
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); // any error message?
StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR"); // any output?
StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT"); // kick them off
errorGobbler.start();
outputGobbler.start(); // any error???
int exitVal = proc.waitFor();
System.out.println("ExitValue: " + exitVal);
} catch (Throwable t) {
t.printStackTrace();
}
}
}
如果運行如下命令上面的代碼會調用word程序
>java GoodWindowExec “abc.doc”
也就是說文件類型如果window能夠識別它就會調用對應的程序處理。
StreamGlobbler的最重要作用是他會清空所有的傳遞給他的inputstream,這樣不會造成Process阻塞或者死鎖。