正确的调用系统命令——为Process.waitFor设置超时以及其他

Java中在阻塞调用系统命令的时候,一般是使用Runtime.getRuntime().exec(command)返回一个process对象,再调用Process.waitFor()来等待命令执行结束,获取执行结果。

然而这样简单的调用也是有坑的,有几个地方需要小心留意。

一个是Runtime.getRuntime().exec(command)这个调用对于可执行文件路径或者参数中有空格等特殊字符的情况不能处理,必须手工对其进行转义。推荐使用ProcessBuilder这个新加入的类来替换Runtime.getRuntime().exec,可以自动处理特殊字符,并且接口也要更加丰富和方便使用。

然后,即使只是调用了很简单的脚本命令,在调用Process.waitFor()后都有可能发生无休止或者接近于无休止的阻塞,所以在代码中加入超时控制是必须的。但是Process.waitFor()本身并不支持超时时间设置,一个方法是改用非阻塞的Process.exitValue()方法,然后轮询检查进程状态,这种方式比较消耗CPU,以至于轮询间隔也不能设置得太小,总归不是很完美。另外就是另起一个线程来调用程序,在主线程中发现超时的时候,直接调用process.destroy()终止进程。

还有就是在捕获程序输出的问题。如果要读取程序的删除结果,一定要在调用Process.waitFor()前将程序的stdout和stderr都读完,否则就有可能因为pipe的缓冲区不够,被调用的系统命令阻塞在标准输出和标准错误输出上。Windows因为这个缓冲区的默认值比较小更容易出现这个问题。
需要注意读取程序的stdout和stderr都是阻塞的操作,这意味着必须在两个线程里分别读取,而不是在一个线程里一次读取,否则还是有可能出现阻塞的情况:比如先读取stdout再读取stderr,如果程序的stderr输出已经填满了缓冲区,程序就会阻塞不继续执行,但是java线程又阻塞在读取stdout上,只有stdout结束了才会去读取stderr。结果就是互相等待着的过程中哦给你程序卡死了。
为了不让程序变得过于复杂,我们可以把程序的stderr重定向到stdout中,这样只要读取stdout一个就好了。缺点就是没有区分出错的输出信息和正常的输出信息。

最后使用的代码如下:

/**
 * 一个进程调用工具.
 *
 * @author dongliu
 *
 */
public  class  ProcessUtils  {

     /**
     * 运行一个外部命令,返回状态.若超过指定的超时时间,抛出TimeoutException
     * 
     */
     public  static  ProcessStatus  execute ( final  long  timeout ,  final  String ...  command )
             throws  IOException ,  InterruptedException ,  TimeoutException  {

         ProcessBuilder  pb  =  new  ProcessBuilder ( command );
         pb . redirectErrorStream ( true );
         Process  process  =  pb . start ();

         Worker  worker  =  new  Worker ( process );
         worker . start ();
         ProcessStatus  ps  =  worker . getProcessStatus ();
         try  {
             worker . join ( timeout );
             if  ( ps . exitCode  ==  ProcessStatus . CODE_STARTED )  {
                 // not finished
                 worker . interrupt ();
                 throw  new  TimeoutException ();
             }  else  {
                 return  ps ;
             }
         }  catch  ( InterruptedException  e )  {
             // canceled by other thread.
             worker . interrupt ();
             throw  e ;
         }  finally  {
             process . destroy ();
         }
     }

     private  static  class  Worker  extends  Thread  {
         private  final  Process  process ;
         private  ProcessStatus  ps ;

         private  Worker ( Process  process )  {
             this . process  =  process ;
             this . ps  =  new  ProcessStatus ();
         }

         public  void  run ()  {
             try  {
                 InputStream  is  =  process . getInputStream ();
                 try  {
                     ps . output  =  Utils . toString ( is );
                 }  catch  ( IOException  ignore )  {  }
                 ps . exitCode  =  process . waitFor ();

             }  catch  ( InterruptedException  e )  {
                 Thread . currentThread (). interrupt ();
             }
         }

         public  ProcessStatus  getProcessStatus ()  {
             return  this . ps ;
         }
     }
    
     public  static  class  ProcessStatus  {
         public  static  final  int  CODE_STARTED  =  - 257 ;
         public  volatile  int  exitCode ;
         public  volatile  String  output ;
     }

}


希望这个方法有用,在调一个把word转成pdf pdf转成swf的项目。。 
在晚上找的 ,还没有测试 一会回家测试看看。


转载:http://www.dongliu.net/post/496142
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值