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. 0-1背包问题(经典)HDU2602 Bone Collector

    Bone Collector Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  2. HDU3605:Marriage Match IV

    Marriage Match IV Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...

  3. bzoj 3160 万径人踪灭 FFT

    万径人踪灭 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1936  Solved: 1076[Submit][Status][Discuss] De ...

  4. SD卡给MCU升级

    目 录1. 前言2. 初识BootLoader2.1 百度百科的BootLoader2.2 BootLoader的简单理解2.3 BootLoader的作用3. BootLoader预备知识3.1 复 ...

  5. 货车运输(LCA+最大生成树)

    神奇传送门 恩,这是一道神奇的LCA+难度的题目. 题目是这样的: A 国有n座城市,编号从1到n,城市之间有 m条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q辆货车在运输货物,司机们 ...

  6. Dom4J读写xml

    解析读取XML public static void main(String[] args) { //1获取SaxReader对象 SAXReader reader=new SAXReader(); ...

  7. AC日记——Two poj 1849

    Two 思路: 树形DP求直径: 答案是边权总和*2-直径: dp[i][1]::以i为根的子树中最长的路径: dp[i][0]::以i为根的子树中次长的路径: 来,上代码: #include < ...

  8. POJ 3253 Fence Repair【哈弗曼树/贪心/优先队列】

    Fence Repair Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 53645   Accepted: 17670 De ...

  9. java序列化的机制与原理

    有关Java对象的序列化和反序列化也算是Java基础的一部分,下面对Java序列化的机制和原理进行一些介绍. Java序列化算法透析 Serialization(序列化)是一种将对象以一连串的字节描述 ...

  10. c# await 到底等待的是什么?

    static void Main(string[] args) { Print(); Console.WriteLine("5 :::" + Thread.CurrentThrea ...