Java ProcessBuilder:结果进程挂起

时间:2010-07-19 21:54:49

标签: java linux crash multithreading processbuilder

我一直在尝试使用Java的ProcessBuilder在Linux中启动应该“长期”运行的应用程序。该程序运行的方式是启动命令(在这种情况下,我正在启动媒体播放应用程序),允许它运行,并检查以确保它没有崩溃。例如,检查PID是否仍处于活动状态,然后重新启动该进程(如果已经死亡)。

我现在遇到的问题是PID在系统中仍然存在,但应用程序的GUI挂起。我尝试将ProcessBuilder(cmd).start()转换为一个单独的线程,但这似乎没有解决任何问题,正如我希望的那样。

基本上结果是,对于用户来说,程序APPEARS已经崩溃,但是杀死了驱动ProcessBuilder.start()进程的Java进程实际上允许创建的进程恢复其正常行为。这意味着Java应用程序中的某些东西干扰了生成的进程,但此时我完全不知道是什么。 (因此,为什么我尝试将它分成另一个线程,似乎没有解决任何问题)

如果有人有任何意见或想法,请告诉我,因为我不能为我的生活想到如何解决这个问题。

编辑:我不关心从Process创建的I / O流,因此没有采取任何措施来解决这个问题 - 这是否会导致流程本身挂起?

10 个答案:

答案 0 :(得分:35)

如果进程写入stderr或stdout,并且你没有读它 - 它只会“挂起”,在写入stdout / err时会阻塞。使用shell将stdout / err重定向到/ dev / null或使用redirectErrorStream(true)合并stdout / err并生成从进程的stdout读取的另一个线程

答案 1 :(得分:12)

你想要 技巧吗?

不要从 ProcessBuilder.start()启动您的流程。不要试图搞乱Java中的流重定向/消费(特别是如果你不给它任何关于它的话);

使用 ProcessBuilder.start()启动一个吞噬所有输入/输出流的小shell脚本。

类似的东西:

#!/bin/bash

nohup $1 >/dev/null 2>error.log &

那就是:如果你不关心stdout并且仍想将stderr(对吗?)记录到文件中( error.log 这里)。

如果您甚至不关心stderr,只需将其重定向到stdout:

#!/bin/bash

nohup $1 >/dev/null 2>1 &

你从Java调用那个小脚本,将它作为一个参数给你想要运行的进程的名称。

如果在Linux上运行的将stdout和stderr重定向到/ dev / null的进程仍然会生成任何,那么你就会遇到一个破坏的,不符合规范的Linux安装;)

换句话说:上面的Just Works [TM]并且摆脱了有问题的“你需要在这个和那个命令中使用流而不是Java特定的无意义”

答案 2 :(得分:11)

如果不处理输出,则运行该进程的线程可能会阻塞。这可以通过生成一个读取进程输出的新线程来完成。

    final ProcessBuilder builder = new ProcessBuilder("script")
                    .redirectErrorStream(true)
                    .directory(workDirectory);

    final Process process = builder.start();
    final StringWriter writer = new StringWriter();

    new Thread(new Runnable() {
        public void run() {
            IOUtils.copy(process.getInputStream(), writer);
        }
    }).start();

    final int exitValue = process.waitFor();
    final String processOutput = writer.toString();

答案 3 :(得分:5)

在我遇到类似问题之后,偶然发现了这一点。同意nos,您需要处理输出。我有这样的事情:

ProcessBuilder myProc2 = new ProcessBuilder(command);
final Process process = myProc2.start();

它工作得很好。生成的进程甚至输出一些输出但不多。当我开始输出更多时,似乎我的过程甚至不再被启动了。我更新到这个:

ProcessBuilder myProc2 = new ProcessBuilder(command);
myProc2.redirectErrorStream(true);        
final Process process = myProc2.start();
InputStream myIS = process.getInputStream();
String tempOut = convertStreamToStr(myIS);

它又开始工作了。 (请参阅此链接以获取convertStreamToStr()代码:http://singztechmusings.wordpress.com/2011/06/21/getting-started-with-javas-processbuilder-a-sample-utility-class-to-interact-with-linux-from-java-program/

答案 4 :(得分:2)

  

编辑:我不关心从Process创建的I / O流,因此没有采取任何措施来解决这个问题 - 这是否会导致流程本身挂起?

如果您没有读取进程创建的输出流,那么一旦应用程序的缓冲区已满,应用程序就可能会阻塞。我从来没有在Linux上看到过这种情况(虽然我并不是说它没有),但我在Windows上看到了这个确切的问题。我认为这可能是相关的。

答案 5 :(得分:2)

JDK7将内置对子进程I / O重定向的支持:

http://download.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html

与此同时,如果你真的想要丢弃stdout / stderr,最好(在Linux上)在一个看起来像这样的命令上调用ProcessBuilder:

["/bin/bash", "-c", "exec YOUR_COMMAND_HERE >/dev/null 2>&1"]

答案 6 :(得分:0)

如果您需要捕获stdout和stderr并监控该过程,那么使用Apache Commons Exec帮助了我很多。

答案 7 :(得分:0)

我认为问题在于Linux本身的缓冲管道。

尝试将stdbuf与您的可执行文件一起使用

new ProcessBuilder().command("/usr/bin/stdbuf","-o0","*executable*","*arguments*");**

-o0表示不缓冲输出。 如果要取消缓冲输入和错误管道,同样适用于-i0-e0

答案 8 :(得分:0)

另一种解决方案是使用Redirect.PIPE开始该过程,然后像这样关闭InputStream

ProcessBuilder builder = new ProcessBuilder(cmd);
builder.redirectOutput(Redirect.PIPE);
builder.redirectErrorStream(true); // redirect the SysErr to SysOut
Process proc = builder.start();
proc.getInputStream().close(); // this will close the pipe and the output will "flow"
procp.waitFor();

我在Windows和Linux上对此进行了测试,并且可以正常工作!

答案 9 :(得分:0)

您需要在等待完成循环之前读取输出。如果输出未填充缓冲区,您将不会收到通知。如果是,它将一直等到您读取输出。

假设您有一些关于您没有阅读的命令的错误或响应。这将导致应用程序停止并等待永远等待。一个简单的方法是将错误重定向到常规输出。

我在这个问题上花了 2 天时间。

public static void exeCuteCommand(String command) {
    try {
        boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
        ProcessBuilder builder = new ProcessBuilder();
        if (isWindows) {
            builder.command("cmd.exe", "/c", command);
        } else {
            builder.command("sh", "-c", command);
        }
        Process process = builder.start();
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        while ((line = reader.readLine()) != null)
            System.out.println("Cmd Response: " + line);
        process.waitFor();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}