You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:14:15