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类,对执行命令的结果进行封装,可以从中获取退出码和输出内容。
      1. public class ExecuteResult {
      2. @Override
      3. public String toString() {
      4. return "ExecuteResult [exitCode=" + exitCode + ", executeOut="
      5. + executeOut + "]";
      6. }
      7.  
      8. private int exitCode;
      9. private String executeOut;
      10.  
      11. public ExecuteResult(int exitCode, String executeOut) {
      12. super();
      13. this.exitCode = exitCode;
      14. this.executeOut = executeOut;
      15. }
      16.  
      17. public int getExitCode() {
      18. return exitCode;
      19. }
      20.  
      21. public void setExitCode(int exitCode) {
      22. this.exitCode = exitCode;
      23. }
      24.  
      25. public String getExecuteOut() {
      26. return executeOut;
      27. }
      28.  
      29. public void setExecuteOut(String executeOut) {
      30. this.executeOut = executeOut;
      31. }
      32.  
      33. }
    • LocalCommandExecutorService 接口,向外暴露executeCommand()方法
      1. public interface LocalCommandExecutorService {
      2. ExecuteResult executeCommand(String[] command, long timeout);
      3. }
    • LocalCommandExecutorServiceImpl 实现类,实现LocalCommandExecutorService 接口的方法
      1. public class LocalCommandExecutorServiceImpl implements
      2. LocalCommandExecutorService {
      3. static final Logger logger = LoggerFactory
      4. .getLogger(LocalCommandExecutorServiceImpl.class);
      5.  
      6. static ExecutorService pool = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
      7. 3L, TimeUnit.SECONDS,
      8. new SynchronousQueue<Runnable>());
      9.  
      10. @Override
      11. public ExecuteResult executeCommand(String[] command, long timeout) {
      12. Process process = null;
      13. InputStream pIn = null;
      14. InputStream pErr = null;
      15. StreamGobbler outputGobbler = null;
      16. StreamGobbler errorGobbler = null;
      17. Future<Integer> executeFuture = null;
      18. try {
      19. process = Runtime.getRuntime().exec(command);
      20. final Process p = process;
      21.  
      22. //close process's output stream.
      23. p.getOutputStream().close();
      24.  
      25. pIn = process.getInputStream();
      26. outputGobbler = new StreamGobbler(
      27. pIn, "OUTPUT");
      28. outputGobbler.start();
      29.  
      30. pErr = process.getErrorStream();
      31. errorGobbler = new StreamGobbler(pErr, "ERROR");
      32. errorGobbler.start();
      33.  
      34. // create a Callable for the command's Process which can be called
      35. // by an Executor
      36. Callable<Integer> call = new Callable<Integer>() {
      37. public Integer call() throws Exception {
      38. p.waitFor();
      39. return p.exitValue();
      40. }
      41. };
      42.  
      43. // submit the command's call and get the result from a
      44. executeFuture = pool.submit(call);
      45. int exitCode = executeFuture.get(timeout,
      46. TimeUnit.MILLISECONDS);
      47. return new ExecuteResult(exitCode, outputGobbler.getContent());
      48. } catch (IOException ex) {
      49. String errorMessage = "The command [" + command
      50. + "] execute failed.";
      51. logger.error(errorMessage, ex);
      52. return new ExecuteResult(-1, null);
      53. } catch (TimeoutException ex) {
      54. String errorMessage = "The command [" + command + "] timed out.";
      55. logger.error(errorMessage, ex);
      56. return new ExecuteResult(-1, null);
      57. } catch (ExecutionException ex) {
      58. String errorMessage = "The command [" + command
      59. + "] did not complete due to an execution error.";
      60. logger.error(errorMessage, ex);
      61. return new ExecuteResult(-1, null);
      62. } catch (InterruptedException ex) {
      63. String errorMessage = "The command [" + command
      64. + "] did not complete due to an interrupted error.";
      65. logger.error(errorMessage, ex);
      66. return new ExecuteResult(-1, null);
      67. } finally {
      68. if(executeFuture != null){
      69. try{
      70. executeFuture.cancel(true);
      71. } catch(Exception ignore){}
      72. }
      73. if(pIn != null) {
      74. this.closeQuietly(pIn);
      75. if(outputGobbler != null && !outputGobbler.isInterrupted()){
      76. outputGobbler.interrupt();
      77. }
      78. }
      79. if(pErr != null) {
      80. this.closeQuietly(pErr);
      81. if(errorGobbler != null && !errorGobbler.isInterrupted()){
      82. errorGobbler.interrupt();
      83. }
      84. }
      85. if (process != null) {
      86. process.destroy();
      87. }
      88. }
      89. }
      90.  
      91. private void closeQuietly(Closeable c) {
      92. try {
      93. if (c != null)
      94. c.close();
      95. } catch (IOException e) {
      96. }
      97. }
      98. }
    • StreamGobbler类,用来包装输入输出流
      1. import java.io.BufferedReader;
      2. import java.io.IOException;
      3. import java.io.InputStream;
      4. import java.io.InputStreamReader;
      5.  
      6. import org.slf4j.Logger;
      7. import org.slf4j.LoggerFactory;
      8.  
      9. public class StreamGobbler extends Thread {
      10. private static Logger logger = LoggerFactory.getLogger(StreamGobbler.class);
      11. private InputStream inputStream;
      12. private String streamType;
      13. private StringBuilder buf;
      14. private volatile boolean isStopped = false;
      15.  
      16. /**
      17. * Constructor.
      18. *
      19. * @param inputStream
      20. * the InputStream to be consumed
      21. * @param streamType
      22. * the stream type (should be OUTPUT or ERROR)
      23. * @param displayStreamOutput
      24. * whether or not to display the output of the stream being
      25. * consumed
      26. */
      27. public StreamGobbler(final InputStream inputStream, final String streamType) {
      28. this.inputStream = inputStream;
      29. this.streamType = streamType;
      30. this.buf = new StringBuilder();
      31. this.isStopped = false;
      32. }
      33.  
      34. /**
      35. * Consumes the output from the input stream and displays the lines
      36. * consumed if configured to do so.
      37. */
      38. @Override
      39. public void run() {
      40. try {
      41. //默认编码为UTF-8,这里设置编码为GBK,因为WIN7的编码为GBK
      42. InputStreamReader inputStreamReader = new InputStreamReader(
      43. inputStream,"GBK");
      44. BufferedReader bufferedReader = new BufferedReader(
      45. inputStreamReader);
      46. String line = null;
      47. while ((line = bufferedReader.readLine()) != null) {
      48. this.buf.append(line + "\n");
      49. }
      50. } catch (IOException ex) {
      51. logger.trace("Failed to successfully consume and display the input stream of type "
      52. + streamType + ".", ex);
      53. } finally {
      54. this.isStopped = true;
      55. synchronized (this) {
      56. notify();
      57. }
      58. }
      59. }
      60.  
      61. public String getContent() {
      62. if(!this.isStopped){
      63. synchronized (this) {
      64. try {
      65. wait();
      66. } catch (InterruptedException ignore) {
      67. }
      68. }
      69. }
      70. return this.buf.toString();
      71. }
      72. }
    • 测试用例
      1. public class LocalCommandExecutorTest {
      2. public static void main(String[] args) {
      3. LocalCommandExecutorService service = new LocalCommandExecutorServiceImpl();
      4. String[] command = new String[]{"ping","127.0.0.1"};
      5. ExecuteResult result = service.executeCommand(command, 5000);
      6. System.out.println("退出码:"+result.getExitCode());
      7. System.out.println("输出内容:"+result.getExecuteOut());
      8. }
      9. }

      输出结果如下:

      直接在命令行执行“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. [codeforces] 449C Jzzhu and Apples

    原题 质因数分解后贪心即可(最后贪2) #include<cstdio> #include<vector> #include<stack> #include< ...

  2. Apache2.4启动时报AH00526错误(Invalid command 'Order')

    在WIN XP下手动配置PHP环境,安装Apache2.4+fastcgi后,重启Apache服务,出现如下提示: AH00526: Syntax error on line 293 of D:/ph ...

  3. 原生方法scrollTo支持滚动特效

    scrollTo默认的是瞬间滚动到坐标位置, 使用配置方法, behavior属性设置为smooth就可以支持平滑滚动了,不过这种方法兼容性不行,并且无法支持配置滚动速率 // 默认滚动效果,瞬时滚动 ...

  4. AGC007 - C Pushing Ball

    Description 题目链接 懒得写详细题意了, 放个链接 \(n\le 2*10^5\) 个球, \(n+1\) 个坑, 排成数轴, 球坑交替. 相邻球-坑距离为等差数列 \(d\). 给定首项 ...

  5. 汕头市队赛 SRM1X T1

    木之本樱 背景 “西瓜是可以种在树上的!”——木之本樱 描述 空地上,一排排的西瓜树拔地而起. 魔法世界里,空地是无限大的.所有的树排成了n条直线,每条直线也是向左右两端无限延伸的. 由于自己姓木(之 ...

  6. VC2010编译libwebsockets

    1. 安装cmake: https://cmake.org/files/v3.6/cmake-3.6.0-win64-x64.msi 2. 下载libwebsocket源码: git clone ht ...

  7. Scala学习随笔——控制语句

    Scala只内置了为数不多的几种程序控制语句:if.while.for.try catch以及函数调用,这是因为从Scala诞生开始就包含了函数字面量,Scala内核没有定义过多的控制结构,而是可以通 ...

  8. linux文件系统之loop环设备--新建一个文件系统并挂载

    1.  /dev目录下有所有已经连接到操作系统上的设备,他们能在/dev里出现就表示他们在硬件层面和系统核心层面被识别了.对于stdin.stdout.zero等设备是可以直接用> <这些 ...

  9. rabbitmq web管理页面无法访问

    安装rabbitmq 之后可以通过默认的15672端口访问web界面进行管理,rabbitmq一些默认端口如下: 4369: epmd, a peer discovery service used b ...

  10. wsgi的学习(1):什么是WSGI

    本文来自:http://www.nowamagic.net/academy/detail/1330310 WSGI,全程是:Web Server Gateway Interface,或者python  ...