FROM:http://segmentfault.com/blog/lidonghao/1190000000372535

前一篇博文中,简单介绍了如何使用Process类来调用命令行的功能,那样使用Process会有一个很大的问题,就是可能会出现无限阻塞的情况,永远都无法返回结果。以下是Process的API说明,注意加粗的部分。

ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获得相关信息。Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。
创建进程的方法可能无法针对某些本机平台上的特定进程很好地工作,比如,本机窗口进程,守护进程,Microsoft Windows 上的 Win16/DOS 进程,或者 shell 脚本。创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin、stdout 和 stderr)操作都将通过三个流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁。

解决进程无限阻塞的方法是在执行命令时,设置一个超时时间,下面提供一个工具类,对Process使用进行包装,向外提供设置超时的接口。

    • ExecuteResult类,对执行命令的结果进行封装,可以从中获取退出码和输出内容。
    •  public class ExecuteResult {
      @Override
      public String toString() {
      return "ExecuteResult [exitCode=" + exitCode + ", executeOut="
      + executeOut + "]";
      } private int exitCode;
      private String executeOut; public ExecuteResult(int exitCode, String executeOut) {
      super();
      this.exitCode = exitCode;
      this.executeOut = executeOut;
      } public int getExitCode() {
      return exitCode;
      } public void setExitCode(int exitCode) {
      this.exitCode = exitCode;
      } public String getExecuteOut() {
      return executeOut;
      } public void setExecuteOut(String executeOut) {
      this.executeOut = executeOut;
      } }
    • LocalCommandExecutorService 接口,向外暴露executeCommand()方法
    •  public interface LocalCommandExecutorService {
      ExecuteResult executeCommand(String[] command, long timeout);
      }
    • LocalCommandExecutorServiceImpl 实现类,实现LocalCommandExecutorService 接口的方法
    •  public class LocalCommandExecutorServiceImpl implements
      LocalCommandExecutorService {
      static final Logger logger = LoggerFactory
      .getLogger(LocalCommandExecutorServiceImpl.class); static ExecutorService pool = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
      3L, TimeUnit.SECONDS,
      new SynchronousQueue<Runnable>()); @Override
      public ExecuteResult executeCommand(String[] command, long timeout) {
      Process process = null;
      InputStream pIn = null;
      InputStream pErr = null;
      StreamGobbler outputGobbler = null;
      StreamGobbler errorGobbler = null;
      Future<Integer> executeFuture = null;
      try {
      process = Runtime.getRuntime().exec(command);
      final Process p = process; //close process's output stream.
      p.getOutputStream().close(); pIn = process.getInputStream();
      outputGobbler = new StreamGobbler(
      pIn, "OUTPUT");
      outputGobbler.start(); pErr = process.getErrorStream();
      errorGobbler = new StreamGobbler(pErr, "ERROR");
      errorGobbler.start(); // create a Callable for the command's Process which can be called
      // by an Executor
      Callable<Integer> call = new Callable<Integer>() {
      public Integer call() throws Exception {
      p.waitFor();
      return p.exitValue();
      }
      }; // submit the command's call and get the result from a
      executeFuture = pool.submit(call);
      int exitCode = executeFuture.get(timeout,
      TimeUnit.MILLISECONDS);
      return new ExecuteResult(exitCode, outputGobbler.getContent());
      } catch (IOException ex) {
      String errorMessage = "The command [" + command
      + "] execute failed.";
      logger.error(errorMessage, ex);
      return new ExecuteResult(-1, null);
      } catch (TimeoutException ex) {
      String errorMessage = "The command [" + command + "] timed out.";
      logger.error(errorMessage, ex);
      return new ExecuteResult(-1, null);
      } catch (ExecutionException ex) {
      String errorMessage = "The command [" + command
      + "] did not complete due to an execution error.";
      logger.error(errorMessage, ex);
      return new ExecuteResult(-1, null);
      } catch (InterruptedException ex) {
      String errorMessage = "The command [" + command
      + "] did not complete due to an interrupted error.";
      logger.error(errorMessage, ex);
      return new ExecuteResult(-1, null);
      } finally {
      if(executeFuture != null){
      try{
      executeFuture.cancel(true);
      } catch(Exception ignore){}
      }
      if(pIn != null) {
      this.closeQuietly(pIn);
      if(outputGobbler != null && !outputGobbler.isInterrupted()){
      outputGobbler.interrupt();
      }
      }
      if(pErr != null) {
      this.closeQuietly(pErr);
      if(errorGobbler != null && !errorGobbler.isInterrupted()){
      errorGobbler.interrupt();
      }
      }
      if (process != null) {
      process.destroy();
      }
      }
      } private void closeQuietly(Closeable c) {
      try {
      if (c != null)
      c.close();
      } catch (IOException e) {
      }
      }
      }
    • StreamGobbler类,用来包装输入输出流
    •  import java.io.BufferedReader;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.InputStreamReader; import org.slf4j.Logger;
      import org.slf4j.LoggerFactory; public class StreamGobbler extends Thread {
      private static Logger logger = LoggerFactory.getLogger(StreamGobbler.class);
      private InputStream inputStream;
      private String streamType;
      private StringBuilder buf;
      private volatile boolean isStopped = false; /**
      * Constructor.
      *
      * @param inputStream
      * the InputStream to be consumed
      * @param streamType
      * the stream type (should be OUTPUT or ERROR)
      * @param displayStreamOutput
      * whether or not to display the output of the stream being
      * consumed
      */
      public StreamGobbler(final InputStream inputStream, final String streamType) {
      this.inputStream = inputStream;
      this.streamType = streamType;
      this.buf = new StringBuilder();
      this.isStopped = false;
      } /**
      * Consumes the output from the input stream and displays the lines
      * consumed if configured to do so.
      */
      @Override
      public void run() {
      try {
      //默认编码为UTF-8,这里设置编码为GBK,因为WIN7的编码为GBK
      InputStreamReader inputStreamReader = new InputStreamReader(
      inputStream,"GBK");
      BufferedReader bufferedReader = new BufferedReader(
      inputStreamReader);
      String line = null;
      while ((line = bufferedReader.readLine()) != null) {
      this.buf.append(line + "\n");
      }
      } catch (IOException ex) {
      logger.trace("Failed to successfully consume and display the input stream of type "
      + streamType + ".", ex);
      } finally {
      this.isStopped = true;
      synchronized (this) {
      notify();
      }
      }
      } public String getContent() {
      if(!this.isStopped){
      synchronized (this) {
      try {
      wait();
      } catch (InterruptedException ignore) {
      }
      }
      }
      return this.buf.toString();
      }
      }
    • 测试用例
    •  public class LocalCommandExecutorTest {
      public static void main(String[] args) {
      LocalCommandExecutorService service = new LocalCommandExecutorServiceImpl();
      String[] command = new String[]{"ping","127.0.0.1"};
      ExecuteResult result = service.executeCommand(command, 5000);
      System.out.println("退出码:"+result.getExitCode());
      System.out.println("输出内容:"+result.getExecuteOut());
      }
      }

      输出结果如下:

      直接在命令行执行“ping 127.0.0.1”,结果如下:

      Apache提供了一个开源库,对Process类进行了封装,也提供了设置超时的功能,建议在项目中使用Apache Commons Exec这个开源库来实现超时功能,除了功能更强大外,稳定性也有保障。

[转载]Process工具类,提供设置timeout功能的更多相关文章

  1. 反射工具类.提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class,被AOP过的真实类等工具函数.java

    import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.ap ...

  2. 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精 确的浮点数运算,包括加减乘除和四舍五入。

    package com.minxinloan.utils; import java.math.BigDecimal; public class Arith { // 源文件Arith.java: /* ...

  3. jackson工具类有动态属性过虑功能

    在业务应用中经常会有指定属性序列化json的需求,C#中这个功能很容易就可以解决:使用lambda重新构造一下匿名对象就可以了.一行代码搞定.java是这样解决的. public JsonMapper ...

  4. 用Java开发一个工具类,提供似于js中eval函数功能的eval方法

    今天在看到<Java疯狂讲义>中一个章节习题: 开发一个工具类,该工具类提供一个eval()方法,实现JavaScript中eval()函数的功能--可以动态运行一行或多行程序代码.例如: ...

  5. C#工具类:Json操作帮助类(转载)

    原文转载自C#工具类:Json操作帮助类_IT技术小趣屋. Json序列化和反序列化在程序开发中时常会遇到,在C#中可以使用很多种方法实现对数据的Json序列化和反序列化,封装一个Json操作工具类来 ...

  6. 【转载】C#工具类:Json操作帮助类

    Json序列化和反序列化在程序开发中时常会遇到,在C#中可以使用很多种方法实现对数据的Json序列化和反序列化,封装一个Json操作工具类来简化相应的操作,该工具类中包含以下功能:对象转JSON.数据 ...

  7. 并发工具类(一)等待多线程的CountDownLatch

    前言   JDK中为了处理线程之间的同步问题,除了提供锁机制之外,还提供了几个非常有用的并发工具类:CountDownLatch.CyclicBarrier.Semphore.Exchanger.Ph ...

  8. Spark中经常使用工具类Utils的简明介绍

    <深入理解Spark:核心思想与源代码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源代码分析>一书正式出版上市 <深入理解Spark:核心思想与源代码分析 ...

  9. Spark中常用工具类Utils的简明介绍

    <深入理解Spark:核心思想与源码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源码分析>一书正式出版上市 <深入理解Spark:核心思想与源码分析> ...

随机推荐

  1. POJ3252 Round Numbers 【数位dp】

    题目链接 POJ3252 题解 为什么每次写出数位dp都如此兴奋? 因为数位dp太苟了 因为我太弱了 设\(f[i][0|1][cnt1][cnt0]\)表示到二进制第\(i\)位,之前是否达到上界, ...

  2. 02 Java 基础语法

    在开始 Java 基本语法之前,先说明 Java 程序的基本规范: 大小写敏感,例如 Person 和 person 是不同的 类名首字母大写,如果类名由多个单词组成,每个单词首字母都大写,例如 He ...

  3. 平面ray trace的数据结构加速

    yy了一个数据结构.. 首先考虑到,平面ray trace对应的scene是planar graph with coordinates,特点是除端点外无相交. 我们考虑对所有端点建立kd-tree,注 ...

  4. HTTP基础--cookie机制和session机制

    1.介绍cookie和session的区别,怎么获取与使用?(这个问题比较开放,可深可浅,现在将这里涉及的主要问题总计如下答案) 答: 一.cookie机制和session机制的区别 cookie机制 ...

  5. 【Apache Nutch系列】Nutch2.0配置安装异常集锦

    1.java.lang.NoClassDefFoundError: org/apache/hadoop/hbase/HBaseConfiguration Exception in thread &qu ...

  6. 创建型设计模式之单例模式(Singleton)

     结构 意图 保证一个类仅有一个实例,并提供一个访问它的全局访问点. 适用性 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时. 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更 ...

  7. AUTOIT3设置用户包含目录

     

  8. 静态链接和动态链接库混用导致的链接错误LINK2005

    对于一个静态链接库L.lib,它的使用者app.exe会静态链接L.lib,意思是app.exe会将L.lib中的代码(app需要的部分,例如函数定义,类的定义等等)链接到app.exe中.   而对 ...

  9. openshift 云平台基于kubernetes

    转载:https://www.kubernetes.org.cn/3208.html 目前红帽的核心产品PaaS平台OpenShift,最初在2012年释出时是使用自家开发的容器调度工具,但在2014 ...

  10. Kubernetes-服务发布

    #发布服务有三种方式,第一种把端口映射到所有节点比较适合发布一些tcp/udp的服务对于应用层像http的会导致无法获取到原始客户端IP,第二种需要用到云服务才行,第三种是ingress http服务 ...