Java通过ProcessBuilder调用批处理启动jar引发阻塞死循环求助
阻塞原因
- 只读取了进程的标准输出流,未处理标准错误流:Windows系统下进程的标准输出和错误流是分开的缓冲区,任意一个缓冲区满了之后,子进程就会被系统挂起停止运行,导致流永远不会返回EOF,
readLine就会无限阻塞。 - 批处理未显式后台启动jar进程:默认
start命令会绑定当前批处理的IO上下文,哪怕jar启动完成,批处理的流也不会正常关闭。 - 未显式调用
cmd.exe执行批处理:直接传递bat文件给ProcessBuilder部分环境下会出现文件关联异常,导致进程启动异常卡死。 - 流读取未指定编码:乱码会导致流读取逻辑异常,长期占用缓冲区触发阻塞。
修复方案
1. 修改Java调用逻辑
核心要做3件事:合并标准输出和错误流、显式调用cmd执行批处理、增加超时逻辑避免无限阻塞。
修改后代码如下:
import java.util.concurrent.TimeUnit; // 省略你的业务代码 ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", "startabx6.bat", abx); pb.directory(new File(System.getProperty("user.dir"))); // 合并标准输出和错误流,只需要读一个流就能拿到所有输出,避免缓冲区满阻塞 pb.redirectErrorStream(true); Process process = pb.start(); // 自动关流的try-with-resources写法,避免流泄漏 try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"))) { String line; while ((line = reader.readLine()) != null) { log.info(line); } } // 最多等30秒,超时直接杀进程,避免无限阻塞 boolean processFinished = process.waitFor(30, TimeUnit.SECONDS); if (!processFinished) { process.destroyForcibly(); log.error("批处理执行超时,已强制终止"); } else { log.info("批处理执行完成,退出码:{}", process.exitValue()); }
2. 修改批处理文件
核心要做2件事:后台启动jar不绑定IO、显式退出批处理释放流。
修改后代码如下:
@ECHO OFF echo %1 set build=%1 echo %build% :: 杀8080端口进程,输出重定向到空避免多余内容占缓冲区 FOR /F "tokens=5 delims= " %%P IN ('netstat -a -n -o ^| findstr :8080') DO TaskKill.exe /PID %%P /F >nul 2>&1 echo "launching abx jar" :: /B参数后台启动java进程,不绑定当前批处理的IO,输出全部重定向到空 start /B java -jar -DServer.port=8080 libs\ABJars\%build% >nul 2>&1 :: 显式退出批处理,返回正常退出码 exit /b 0
额外注意事项
- 如果需要保留jar的运行日志,把start命令里的
>nul 2>&1改成>abx-jar.log 2>&1即可,日志会输出到指定文件,不会绑定到批处理的IO流 - 超时时间可以根据你的实际场景调整,比如端口杀进程慢的话可以改成60秒
- 如果你的Windows Server是英文环境,把读取流的编码改成
ISO-8859-1即可
内容的提问来源于stack exchange,提问作者Venkatesh Singh
相关产品推荐
相关产品推荐

