posts - 200, comments - 8, trackbacks - 0, articles - 0

在java中使用Runtime.exec()时要注意的问题

Posted on 2014-04-08 15:20 鑫龙 阅读(3083) 评论(0)  编辑 收藏 引用 所属分类: Java

程序如下:

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阻塞或者死锁。


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理