网上竟然搜不到详细一点的帖子,估计大家用JAVA去管理进程的场景比较少吧,只好自己总结一个。
Java管理进程,API级别是使用:Runtime.getRuntime().exec(“shell”);这个方法。
Java在执行命令时输出到某个Buffer里,这个Buffer是有容量限制的,如果满了一直没读取,就会一直等待,造成进程锁死的现象。
使用Apache Commons Exec,应该可以避免很多类似的坑。
它提供一些常用的方法用来执行外部进程,另外,它提供了监视狗Watchdog来设监视进程的执行超时,同时也还实现了同步和异步功能,
Apache Commons Exec涉及到多线程,比如新启动一个进程,Java中需要再开三个线程来处理进程的三个数据流,分别是标准输入,标准输出和错误输出。
1.基本用法
就三步:
1)创建命令行:CommandLine
2)创建执行器:DefaultExecutor
3)用执行器执行命令:executor.execute(cmdLine);
String cmdStr = "ping www.baidu.com -t";
final CommandLine cmdLine = CommandLine.parse(cmdStr);
DefaultExecutor executor = new DefaultExecutor();
int exitValue = executor.execute(cmdLine);
关于这个exitValue:
一般的进程,其运行结束后,默认值为0。
对于某些有特殊退出值的程序,需要确定知道其值是什么,然后用executor.setExitValue(退出值);进行明确设置,否则,会出现ExecuteException。
比如,写一个JAVA程序,在main函数中,用System.exit(10);退出程序,用Exec管理这个进程就必须设置:executor.setExitValue(10);
如果程序有多个退出值,可使用executor.setExitValues(int[]);函数进行处理。
1.1 通过添加参数方式构建命令。这是官方推荐的方式!
上面的程序可以改为:
final CommandLine cmdLine = new CommandLine("ping");
cmdLine.addArgument("www.baidu.com");
cmdLine.addArgument("-t");
DefaultExecutor executor = new DefaultExecutor();
int exitValue = executor.execute(cmdLine);
2.超时管理–进程执行时间的管理
设置外部命令执行等待时间,如果超过设置的等待时间,则中断执行。
final CommandLine cmdLine = CommandLine.parse("ping www.baidu.com -t");
ExecuteWatchdog watchdog = new ExecuteWatchdog(5000);//设置超时时间:5秒
DefaultExecutor executor = new DefaultExecutor();
executor.setWatchdog(watchdog);
executor.setExitValue(1);//由于ping被到时间终止,所以其默认退出值已经不是0,而是1,所以要设置它
int exitValue = executor.execute(cmdLine);
3.非阻塞方式执行进程
上面的执行外部命令都是阻塞式,也就是在执行外部命令时,当前线程是阻塞的。
比如执行ping -t,当成的JAVA程序会一直等着不会停止。
解决办法:使用DefaultExecuteResultHandler处理外部命令执行的结果,释放当前线程。
final CommandLine cmdLine = CommandLine.parse("ping www.baidu.com -t");
final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
DefaultExecutor executor = new DefaultExecutor();
executor.execute(cmdLine, resultHandler);
//这里开始的代码会被立即执行下去,因为上面的语句不会被阻塞。
resultHandler.waitFor(5000);//等待5秒。
可以使用waitFor来阻塞处理逻辑,比如上面的代码,在执行到waitFor时,会等待5秒再继续执行。
之后,可以通过resultHandler.hasResult()、resultHandler.getExitValue()、resultHandler.getException()获得需要信息。
注意:getException();得到的是Exec自己的异常,不是应用程序(比如JAVA)代码里面抛出的异常。
4.终止进程
通过Watchdog,可以终止正在运行的进程。
final CommandLine cmdLine = CommandLine.parse("ping www.baidu.com -t");
final ExecuteWatchdog watchdog = new ExecuteWatchdog(Integer.MAX_VALUE);
final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
DefaultExecutor executor = new DefaultExecutor();
executor.setWatchdog(watchdog);
executor.execute(cmdLine, resultHandler);
Thread.sleep(10000);//等进程执行一会,再终止它
System.out.println("--> Watchdog is watching ? " + watchdog.isWatching());
watchdog.destroyProcess();//终止进程
System.out.println("--> destroyProcess done.");
System.out.println("--> Watchdog is watching ? " + watchdog.isWatching());
System.out.println("--> Watchdog should have killed the process : " + watchdog.killedProcess());
System.out.println("--> wait result is : " + resultHandler.hasResult());
System.out.println("--> exit value is : " + resultHandler.getExitValue());
System.out.println("--> exception is : " + resultHandler.getException());
resultHandler.waitFor(5000);//等待5秒。下面加上上面的几个System.out,看看进程状态是什么。
5.获得进程的输出信息
可以在程序中,通过PumpStreamHandler,截获进程的各种输出,包括output 和 error stream。
String cmdStr = "ping www.baidu.com";
final CommandLine cmdLine = CommandLine.parse(cmdStr);
DefaultExecutor executor = new DefaultExecutor();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
executor.setStreamHandler(new PumpStreamHandler(baos, baos));
executor.setExitValue(1);
int exitValue = executor.execute(cmdLine);
final String result = baos.toString().trim();
System.out.println(result);//这个result就是ping输出的结果。如果是JAVA程序,抛出了异常,也被它获取。