《转》Java Process应用之惑

1,438 阅读3分钟

很多时候,我们需要调用系统命令来做些处理。比如,在程序中ping设备是否能连接,执行数据库的自动备份,以及程序的重启。这时候我们必须要使用Process类来完成这些功能。 一般情况下,我们都会将命令执行过程中的信息输出,以便检查问题。但有时候我们还需知道这个执行的进程在什么时候结束,因为不仅要知道结束了,还要知道该进程完成时返回的结果。 可能会说,这些不都是API已经给提供好的吗?过程中的消息可以用process.getInputStream()获取,进程最终结果可以由process.waitFor()得到。的确,这些看似可以办到,但其实,里面有陷阱。

首先以Runtime来创建我们需要的Process对象例子:

Java代码 收藏代码

Process process = null;  
        BufferedReader reader=null;  
        try {  
            process = Runtime.getRuntime().exec("ping 192.168.0.125");  
            reader=new BufferedReader(new InputStreamReader(process.getInputStream()));  
            String line=null;  
            while((line=reader.readLine())!=null){  
                System.out.println(line);  
            }  
            int result=process.waitFor();  
            System.out.println(result);  
        } catch (IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        } catch (InterruptedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  

这个例子我们只是简单的ping了一个IP,用getInputStream()输出过程信息,然后用process.waitFor()得到执行完成后的结果。会发现一点问题都没有。 1.此时如果把process.getInputStream()换成process.getErrorStream(),就只有一个结果输出了。 2.如果把执行的内容换成EXP导个oralce数据库的命令呢!发现过程信息和结果值都有,但是如果拿到一个server上去跑跑,又发现总是会报错,rocess has not exited 进程未停止....在网上查询,很多人会告诉你是输出流导致进程阻塞,发生死锁了。 3.此时你再把process.getErrorStream()改回process.getInputStream(),发现啥也没有,而且程序不会停掉。这是肯定的,输出流又导致进程阻塞了。 查API得知Process所有标准 io(即 stdin,stdout,stderr)操作都将通过三个流 (getOutputStream(),getInputStream(),getErrorStream()) 重定向到父进程。那为什么执行系统自带命令就能用getInputStream()获取到信息而不是系统自带命令就一定要用getErrorStream()来获取信息呢!惑之...待高手解答... 获取进程返回结果有两个方法exitValue()和waitFor()。往往会发现流的堵塞无法使其得到值就报异常了。这个网上也有解答方法。即要清空getInputStream()和getErrorStream()这两个流。而且两个流的清空一定是异步的。 Java代码 收藏代码

static void drainInBackground(final InputStream is) {  
            new Thread(new Runnable(){  
                public void run(){  
                    try{  
                        while( is.read() >= 0 );  
                    } catch(IOException e){   
                        // return on IOException                  
                    }  
                }  
            }).start();  
        }  



有没有好的办法不写这个清空流的方法呢!或是还不明白,那就直接用ProcessBuilder来创建Process对象吧!ProcessBuilder已经给出了这方面的解决方案,但是必须要注意的是ProcessBuilder的redirectErrorStream方法。查API可知晓,redirectErrorStream方法设置为ture的时候,会将getInputStream(),getErrorStream()两个流合并,自动会清空流,无需我们自己处理。如果是false,getInputStream(),getErrorStream()两个流分开,就必须自己处理,程序如下:

Java代码 收藏代码

try {  
            ProcessBuilder pbuilder=new ProcessBuilder("ping","192.168.0.125");  
            pbuilder.redirectErrorStream(true);  
            process=pbuilder.start();  
            reader=new BufferedReader(new InputStreamReader(process.getInputStream()));  
            String line=null;  
            while((line=reader.readLine())!=null){  
                System.out.println(line);  
            }     
            int result=process.waitFor();  
            System.out.println(result);  
        } catch (IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        } catch (InterruptedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  

现在无论你调用的是系统自带命令还是配置环境变量的其他命令,getInputStream()流都能给你过程信息和执行结果。 如果redirectErrorStream设置为false,那结果会和上面所说一样。

最后,还要说的是,得到的process.waitFor()结果。别以为这个执行结果值是一层不变的0,API没有给出具体有多少种类型的返回值,就我测试的结果来看: 0 successfully 1 failure 3 successfully! but a warning ....2没有测试出来

经过一个上午的不断测试,找源码来看,查资料,终于将这个已经没有认真理会的API缺陷透彻的梳理了一遍。 为了不重复别的话,很多基础知识没有描述,本着重点解决问题和存在的问题。