1 遇到的问题: java启动一个外部程序,外部程序一直挂起不退出。
2 解决方法:启动两个线程, 分别读取外部程序的标准输出和标准错误输出。
3 参考代码:
2 解决方法:启动两个线程, 分别读取外部程序的标准输出和标准错误输出。
3 参考代码:
String db3File = FileUtil.connectFile(workDirStr, fileName);
String cmd = "java -jar " + jarFile.getAbsolutePath() + " "
+ fileName;// + " debug" ; //db3 需要相对路径 可以添加debug参数查看生成文件
String expectZipFile = db3File+".4.zip";//期望的zip文件
logger.info("DB3生成zip包, cmd={}, 期望生成zip文件={}, workDir={}", cmd, expectZipFile, workDir);
Process process = Runtime.getRuntime().exec(cmd, null, workDir);
// 读取错误流和正常流的输入,否则会阻塞,不能正确获得结果
InputStream stderr = process.getErrorStream(); //获取标准错误输出流
getInputData(stderr, true);
InputStream inpbuildtar = process.getInputStream();//获取标准输出流
getInputData(inpbuildtar, false);
boolean isProcessFinsh = process.waitFor(20, TimeUnit.SECONDS);
if (!isProcessFinsh) {
logger.info("DB3转换zip包, MapMaker.jar--失败, 程序20秒未执行完毕, workDir={}", workDirStr);
return null;
}
/**
* 读流中数据
*
* @param inputStream
*/
private static void getInputData(final InputStream inputStream, boolean isError) {
//新开一个线程来读取创建的进程日志 必须这样 http://blog.csdn.net/mengxingyuanlove/article/details/50707746
Thread thread = new Thread(new Runnable(){
public void run(){
logger.info("启动读取进程流, isError={}", isError);
try {
String msg = IOUtils.toString(inputStream, Charset.defaultCharset());
if (isError) {
logger.error("DB3转换zip包, MapMaker.jar 返回={}", msg);
} else {
logger.info("DB3转换zip包, MapMaker.jar 返回={}", msg);
}
} catch (IOException e) {
logger.error("DB3转换zip包, MapMaker.jar Error, errorMsg=" + e.getMessage(), e);
} finally {
IOUtils.closeQuietly(inputStream);
}
logger.info("结束读取进程流, isError={}", isError);
}
});
thread.start();
}
4 问题的分析
查看java.lang.Process.getInputStream()的帮助文档,有一句描述"Returns the input stream connected to the normal output of the subprocess."。方法返回的输入流是连接到子进程的输出流。
如上图,子进程(就是你使用java启动的进程)向输出流写数据的时候,会放到缓冲区中,然后java进程通过对应的输入流读取。但是当缓冲区满了,子进程就会停住(现象就是子进程不退出)。
如果你的java程序不读取数据,当子进程输出少时,因为有缓存的存在,子进程不会阻塞。但是当子进程输出很多,缓冲区满了,就会阻塞了。
错误输出流同理。