Java进程在Spring MVC中调用getRuntime().exec()时卡住的问题
问题分析与解决方案
我之前也碰到过一模一样的场景,结合你的代码和运行环境来看,主要是这几个核心原因导致的:
1. 未及时读取进程输出/错误流,导致缓冲区阻塞
这是最常见的问题!当你用Runtime.exec()启动Shell进程时,该进程的标准输出和错误输出会被暂存在系统缓冲区里,如果这些缓冲区被填满,进程就会暂停执行,等着Java端读取缓冲区内容——在外层看起来就像是卡在了exec()调用这一步。
你手动运行程序时,控制台会自动处理这些输出流,但在Spring MVC的Web容器环境中,这些流没人主动读取,直接就把进程堵死了。
修复代码示例:
要在调用waitFor()之前,就启动独立线程去读取两个流,而不是等进程结束后再处理:
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.util.concurrent.TimeUnit; public class Test { public static void main(String[] args) { try { StringBuffer output = new StringBuffer(); Process p; System.out.println("Before Runtime"); p = Runtime.getRuntime().exec("bash /home/username/script.sh"); System.out.println("After Runtime.exec"); // 启动线程读取标准输出 new Thread(() -> readStream(p.getInputStream(), output, false)).start(); // 启动线程读取错误输出 new Thread(() -> readStream(p.getErrorStream(), output, true)).start(); // 等待进程结束 boolean isFinished = p.waitFor(4, TimeUnit.MINUTES); System.out.println("After p.waitFor(), process finished: " + isFinished); } catch (Exception e) { System.out.println("Error: " + e.getMessage()); e.printStackTrace(); } } private static void readStream(InputStream is, StringBuffer output, boolean isError) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) { String line; while ((line = reader.readLine()) != null) { output.append(line + "\n"); if (isError) { System.out.println("ErrorLine: " + line); } else { System.out.println(line); } } } catch (Exception e) { e.printStackTrace(); } } }
2. Web容器运行用户权限不足
Spring MVC通常跑在Tomcat、Jetty这类容器里,容器的默认运行用户(比如tomcat用户)可能没有执行/home/username/script.sh的权限,或者无法访问脚本里依赖的文件/目录,导致进程启动后直接卡住。
修复方案:
- 给脚本添加其他用户的执行权限:
chmod o+x /home/username/script.sh - 调整脚本所在目录的访问权限:
chmod o+x /home/username/ - 更安全的方式是把脚本复制到容器有权限的目录(比如
/var/lib/tomcat9/scripts/),避免直接操作个人目录
3. 环境变量差异导致脚本执行异常
手动运行时的环境变量(比如PATH、自定义变量)和Web容器的环境变量完全不同,脚本里依赖的命令可能在容器环境中找不到,进而导致进程卡住。
修复方案:
- 在脚本里使用命令的绝对路径(比如用
/usr/bin/git而不是只写git) - 或者在
exec()调用时手动指定环境变量:
// 复制当前系统环境变量,可按需添加自定义变量 String[] envVars = System.getenv().entrySet().stream() .map(entry -> entry.getKey() + "=" + entry.getValue()) .toArray(String[]::new); p = Runtime.getRuntime().exec("bash /home/username/script.sh", envVars);
额外排查小技巧
- 先在控制器里执行一个极简脚本(比如
echo "test"),验证exec()是否能正常运行,排除基础调用问题 - 查看Web容器的日志(比如Tomcat的
catalina.out),里面可能藏着没被捕获的错误信息
内容的提问来源于stack exchange,提问作者Shubham Sharma
相关产品推荐
相关产品推荐

