hellsing42 发表于 2013-2-4 22:10:02

StreamGobbler的详细解释

这是摘自《More Java pitfalls 中文版》上的例子。

先请编译和运行下面程序:
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();         }   } }   
我们知道javac命令,当不带参数运行javac 程序时,它将输出帮助说明,为什么上面程序不产生任何输出并挂起,永不完成呢?java文档上说,由于有些本地平台为标准输入和输出流所提供的缓冲区大小有限,如果不能及时写入子进程的输入流或者读取子进程的输出流,可能导致子进程阻塞,甚至陷入死锁。所以,上面的程序应改写为:
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();         }   } }

下面是正确的输出:

D:\java>java   MediocreExecJavac
<ERROR>
Usage: javac <options> <source files>
where possible options include:
-g                         Generate all debugging info
-g:none                  Generate no debugging info
-g:{lines,vars,source}   Generate only some debugging info
-nowarn                  Generate no warnings
-verbose                   Output messages about what the compiler is doing
-deprecation               Output source locations where deprecated APIs are used
-classpath <path>          Specify where to find user class files
-cp <path>               Specify where to find user class files
-sourcepath <path>         Specify where to find input source files
-bootclasspath <path>      Override location of bootstrap class files
-extdirs <dirs>            Override location of installed extensions
-endorseddirs <dirs>       Override location of endorsed standards path
-d <directory>             Specify where to place generated class files
-encoding <encoding>       Specify character encoding used by source files
-source <release>          Provide source compatibility with specified release

-target <release>          Generate class files for specific VM version
-version                   Version information
-help                      Print a synopsis of standard options
-X                         Print a synopsis of nonstandard options
-J<flag>                   Pass <flag> directly to the runtime system

</ERROR>
Process exitValue: 2

D:\java>


下面是一个更一般的程序,它用两个线程同步清空标准错误流和标准输出流,并能根据你所使用的windows操作系统选择windows命令解释器command.com或cmd.exe,然后执行你提供的命令。


import java.util.*; import java.io.*; class StreamGobbler extends Thread {   InputStream is;   String type;//输出流的类型ERROR或OUTPUT          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);               System.out.flush();             }             } 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" );             System.out.println("osName: " + osName);             String[] cmd = new String;             if(osName.equals("Windows XP") ||osName.equals("Windows 2000"))             {               cmd = "cmd.exe" ;               cmd = "/C" ;               cmd = args;             }             else if( osName.equals( "Windows 98" ) )             {               cmd = "command.com" ;               cmd = "/C" ;               cmd = args;             }                        Runtime rt = Runtime.getRuntime();             System.out.println("Execing " + cmd + " " + cmd+ " " + cmd);             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();         }   } }

下面是一个测试结果:

D:\java>javaGoodWindowsExec "copy Test.java Test1.java"
osName: Windows XP
Execing cmd.exe /C copy Test.java Test1.java
OUTPUT>已复制         1 个文件。
ExitValue: 0

D:\java>

下面的测试都能通过(windows xp+jdk1.5)

D:\java>java   GoodWindowsExec dir

D:\java>java   GoodWindowsExec Test.java

D:\java>java   GoodWindowsExec regedit.exe

D:\java>java   GoodWindowsExec NOTEPAD.EXE

D:\java>java   GoodWindowsExec first.ppt

D:\java>java   GoodWindowsExec second.doc


以上的意思是说如果不使用StreamGobbler把inputstream清空的话,就会造成进程被堵死,从而导致waitFor()一直等下去,永远结束不了.所以StreamGobbler作为单独的线程会自动清理inputstream防止他被堵住
页: [1]
查看完整版本: StreamGobbler的详细解释