直接上出问题的代码:
public CommandResult executeFile(File file) {
ArrayList<String> stdoutList = new ArrayList<String>();
ArrayList<String> stderrList = new ArrayList<String>();
try {
Process p = Runtime.getRuntime().exec("cmd /c " + file.getName(), null, file.getParentFile());
p.waitFor();
p.getOutputStream().flush();
p.getOutputStream().close();
newThreadInputStream(p.getInputStream(), stdoutList).start();
//newThreadInputStream(p.getErrorStream(), stderrList).start();
} catch (IOException | InterruptedException e) {
LOG.error(e.getMessage(), e);
throw new RuntimeException(e);
}
return new CommandResult(stdoutList, stderrList);
}
该方法,是通过Runtime.getRuntime().exec
的方式去执行一个批处理文件,代码中可以看到,标准输入流是开启了一个线程去处理了,但是错误流部分的处理给注释掉了。然后,坑人的部分就开始了
在我本地去调用该方法执行批处理文件时,没有任何问题,然后在别人的环境执行时出现了线程阻塞的问题,通过加日志,发现线程在p.waitFor()
这里阻塞住了,但我本地又没有事情。
通过查阅资料,得知,waitFor()
的位置应该放在对输入流及错误流处理的后边执行,并且对输入流和错误流都要开启线程去处理,否则导致JVM缓冲区满了之后,就会出现阻塞。
修改代码后:
@Override
public CommandResult executeFile(File file) {
ArrayList<String> stdoutList = new ArrayList<String>();
ArrayList<String> stderrList = new ArrayList<String>();
ArrayList<String> list = new ArrayList<String>();
Process p = null;
try {
p = Runtime.getRuntime().exec("cmd /c " + file.getName(), null, file.getParentFile());
p.getOutputStream().flush();
p.getOutputStream().close();
newThreadInputStream(p.getInputStream(), stdoutList).start();
newThreadInputStream(p.getErrorStream(), stderrList).start();
LOG.info("waitFor---before");
p.waitFor();
LOG.info("waitFor---after");
} catch (IOException | InterruptedException e) {
LOG.error("",e);
throw new RuntimeException(e);
} finally {
try {
p.destroy();
} catch (Exception e){
LOG.error("",e);
}
}
return new CommandResult(stdoutList, list);
}
修改后,再次执行,正常。
其实这里我们在通过process执行批处理文件时,可以使用ProcessBuilder
而不是Runtime.getRuntime().exec()
,ProcessBuilder
的好处就是可以合并错误流和输入流可以合并
ProcessBuilder processBuilder = new ProcessBuilder(file + File.separator+"backup.bat");
//将error流合并到标准中
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
同样的,我们也需要去单开一个线程去处理这个流
process.getInputStream()