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. 【CZY选讲·逆序对】

    题目描述 LYK最近在研究逆序对. 这个问题是这样的. 一开始LYK有一个2^n长度的数组ai. LYK有Q次操作,每次操作都有一个参数k.表示每连续2^k长度作为一个小组.假设 n=4,k= ...

  2. python登录qq

    登录qq的用的是get方法, 首先抓login_sig(某个包中的cookie),接着验证码的包(包含对验证码的校验),,最后计算一个p的加密算法,接着再get请求一个链接 https://ssl.p ...

  3. Java并发(1)- 聊聊Java内存模型

    引言 在计算机系统的发展过程中,由于CPU的运算速度和计算机存储速度之间巨大的差距.为了解决CPU的运算速度和计算机存储速度之间巨大的差距,设计人员在CPU和计算机存储之间加入了高速缓存来做为他们之间 ...

  4. BZOJ 4078: [Wf2014]Metal Processing Plant

    4078: [Wf2014]Metal Processing Plant Time Limit: 100 Sec  Memory Limit: 128 MBSubmit: 86  Solved: 20 ...

  5. HDU5037 Frog

    Once upon a time, there is a little frog called Matt. One day, he came to a river. The river could b ...

  6. php+ajax+jquery分页并显示数据

    参考https://www.helloweba.com/view-blog-195.html html页面 <div class="weui-cells" id=" ...

  7. Linux单机安转Spark

    安装Spark需要先安装jdk及安装Scala. 1. 创建目录 > mkdir  /opt/spark > cd  /opt/spark 2. 解压缩.创建软连接 > tar  z ...

  8. RobotFramework自动化3-搜索案例【转载】

    本篇转自博客:上海-悠悠 原文地址:http://www.cnblogs.com/yoyoketang/tag/robotframework/ 前言 RF系列主要以案例为主,关键字不会的可以多按按F5 ...

  9. postgres 基本使用

    postgres=# help 您正在使用psql, 这是一种用于访问PostgreSQL的命令行界面 键入: \copyright 显示发行条款 \h 显示 SQL 命令的说明 \? 显示 pgsq ...

  10. hdu 5720(贪心+区间合并)

    Wool Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Subm ...