首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >运行linux命令时出现Tomcat问题

运行linux命令时出现Tomcat问题
EN

Stack Overflow用户
提问于 2014-10-07 13:16:40
回答 1查看 167关注 0票数 1

我正在尝试用java代码运行linux命令。该命令是raspivid,我已经将其放在服务器上的test.sh文件下,用于实时摄像头流媒体。一切正常,但问题是流在启动tomcat服务器几分钟后停止。与流一样,在java中运行命令6-7分钟后停止,但raspivid进程在后台运行。另一方面,当我不使用java代码运行相同的命令时,它工作得很好。这是tomcat堆的问题还是其他导致流停止的问题?请帮助查看以下代码:

代码语言:javascript
复制
try {
         Process p = Runtime.getRuntime().exec(new String[]{"sudo","sh","/home/pi/test.sh"});
            BufferedReader stdInput = new BufferedReader(new
                 InputStreamReader(p.getInputStream()));

            BufferedReader stdError = new BufferedReader(new
                 InputStreamReader(p.getErrorStream()));

            // read the output from the command
            LOGGER.info("Here is the standard output of the command:\n");
            while ((s = stdInput.readLine()) != null) {
                LOGGER.info(s);
            }

            // read any errors from the attempted command
            LOGGER.info("Here is the standard error of the command (if any):\n");
            while ((s = stdError.readLine()) != null) {
                LOGGER.info(s);
            }

        }
EN

回答 1

Stack Overflow用户

发布于 2014-10-07 13:37:31

问题是BufferedReader.readLine()会一直阻塞,直到可以读取一整行(以任何行尾字符序列结尾),并且您不会“并行”读取进程的两个输出,如果它的缓冲区被填满,进程就会被阻塞。

您需要读取进程输出的数据。

一个进程有两个输出流:标准输出和错误输出。您必须读取这两个,因为进程可能会同时写入这两个输出。

进程的输出流有缓冲区。如果输出流的缓冲区已满,则会阻止进程向其中写入更多数据的尝试。

这样做:

代码语言:javascript
复制
BufferedReader stdInput = new BufferedReader(
        new InputStreamReader(p.getInputStream()));

BufferedReader stdError = new BufferedReader(
        new InputStreamReader(p.getErrorStream()));

while (p.isAlive()) {
    while (stdInput.ready())
        LOGGER.info(stdInput.readLine());

    while (stdError.ready())
        LOGGER.info(stdError.readLine());

    Thread.sleep(1);
}

此解决方案的问题(并修复它):

此解决方案存在错误。该进程可能不会将整行写入其输出。如果是这种情况,进程可能仍然挂起,例如,如果它将1个字符写入其标准输出,则stdInput.readLine()将阻塞(因为它将读取,直到遇到新的行字符),并且如果该进程将继续写入其错误流,则当错误流的缓冲区已满时,该进程将被阻塞。

因此,最好不要按行读取buffer的输出流,而是按字符读取(当然,这会增加日志记录的难度):

代码语言:javascript
复制
StringBuilder lineOut = new StringBuilder(); // std out buffer
StringBuilder lineErr = new StringBuilder(); // std err buffer

while (p.isAlive()) {
    while (stdInput.ready()) {
        // Append the character to a buffer or log if it is the line end
        char c = (char) stdInput.read();
        if (c == '\n') {  // On UNIX systems line separator is one char: '\n'
            LOGGER.info(lineOut.toString());
            lineOut.setLength(0);
        }
        else
            lineOut.append(c);
    }

    while (stdError.ready()) {
        // Append the character to a buffer or log if it is the line end
        char c = (char) stdError.read()
        if (c == '\n') {  // On UNIX systems line separator is one char: '\n'
            LOGGER.info(lineErr.toString());
            lineErr.setLength(0);
        }
        else
            lineErr.append(c);
    }

    Thread.sleep(1);
}

替代(更干净、更简单)的解决方案

或者,您可以启动两个线程,一个用于读取进程的标准输出,另一个用于读取进程的标准错误。这可以简化事情:

代码语言:javascript
复制
private static void consumeStream(final InputStream is) {
    new Thread() {
        @Override
        public void run() {
            BufferedReader r = new BufferedReader(new InputStreamReader(is));
            String line;
            while ((line = r.readLine()) != null)
                LOGGER.info(line);
        }
    }.start();
}

并使用它:

代码语言:javascript
复制
consumeStream(p.getInputStream());
consumeStream(p.getErrorStream());
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26228993

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档