最近在做一项目,其中有一模块需要在页面上配置第三方提供的程序路径,配置完成后系统定时执行(quartz),也可以在页面上手动执行,ajax调用后台方法,执行第三方提供的程序,只要是读取固定格式的文件解析成图片,存到本地目录和数据库中。当要解析的文件很大的时候,就会遇到啤酒爆炸的问题。。。
File file = new File(pro);
if (file.exists()) {
logger.info("seed程序开始执行....");
ProcessBuilder procBuilder = new ProcessBuilder(file.getAbsolutePath());
procBuilder.redirectErrorStream(true); //合并输出子进程的standard和error inputstream
logger.info("合并子进程InputStream{}",procBuilder.redirectErrorStream());
Process process = procBuilder.start();
drainInBackground(process.getInputStream());
int exit = process.waitFor();
logger.info("seed程序退出{}", exit);
josnResult = "exeok";
} else {
logger.info("seed程序未找到....");
josnResult = "notfound";
}
/**
* 启动一个线程,用于排空子进程的输出流,否则子进程会挂起
*/
static void drainInBackground(final InputStream is) {
new Thread(new Runnable() {
public void run() {
try {
while (is.read() >= 0)
;
} catch (IOException e) {
// return on IOException
}
}
}).start();
}
这里的教训是: 为了确保子进程能够结束,你必须排空它的输出流;对于错误流(error stream)也是一样,而且它可能会更麻烦,因为你无法预测进程什么时候会倾 倒(dump)一些输出到这个流中。在 5.0 版本中,加入了一个名为ProcessBuilder 的类用于排空这些流。它的 redirectErrorStream 方法将各个流合并起来,所以你只需要排空这一个流。如果你决定不合并输出流和错误流,你必须并行地 (concurrently)排空它们。试图顺序化地(sequentially)排空它们会导致子进程被挂起。