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. POJ 1061 青蛙的约会 | 同余方程和exGcd

    题解: 要求s+px=t+qx (mod L) 移项 (p-q)x=t-s (mod L) 等价于 (p-q)x+Ly=t-s 即ax+by=c的方程最小非负根 exGcd后乘个C #include& ...

  2. jstl标签forEach的用法--遍历java的集合

    再讲<c:forEach>之前,现讲一下让EL表达式生效的语句 <% @ page isELIgnored="false"%>这句语句在你想让EL表达式生效 ...

  3. 【ZOJ4061】Magic Multiplication(构造)

    题意:定义一个新运算为两个数A,B上每一位相乘,然后顺次接在一起,现在给定结果C和原来两个数字的长度,要求恢复成原来的数字A,B 若有多解输出A字典序最小的,A相同输出B字典序最小的,无解输出Impo ...

  4. Synthesis of memory barriers

    A framework is provided for automatic inference of memory fences in concurrent programs. A method is ...

  5. C#实时读取数据----局部页面刷新【转】

    I)现在刚开始学习C#,对一些基本的控件了解的不够,有个实时监控的系统,需要页面中的数据每5秒钟刷新一次, 要是每5秒钟页面全部的刷新,那页面根本就没法看了,对这个问题在CSDN上也专门开了帖子,问了 ...

  6. Weblogic 监控工具汇总及简介

    https://blog.csdn.net/hualusiyu/article/details/39608637

  7. react 使用Form组件如何清空上一次操作

    最近在做一个表单联查时候,总是会发现后一个选择器会记住上一次选择的值 ,这会导致前一级选择器已经做出更新后,后一级选择器却还记住上一次的操作, 这里有个方法可以在上级选择器事件操作时清空Form组件的 ...

  8. 计蒜客 28206.Runway Planning (BAPC 2014 Preliminary ACM-ICPC Asia Training League 暑假第一阶段第一场 F)

    F. Runway Planning 传送门 题意简直就是有毒,中间bb一堆都是没用的,主要的意思就是度数大于180度的就先减去180度,然后除以10,四舍五入的值就是答案.如果最后结果是0就输出18 ...

  9. (3)WPF 布局

    一.布局原则 二.布局过程 三.布局容器 核心布局面板 布局属性

  10. (9)oracle 表的基本查询

    转到进阶查询 查看表的结构 desc  表名; desc student; 查看整张表的数据 select * from 表名; //查整张表很耗时间 select* from student; 查看 ...