我一直在尝试使用Java的ProcessBuilder在Linux中启动应该“长期”运行的应用程序。该程序运行的方式是启动命令(在这种情况下,我正在启动媒体播放应用程序),允许它运行,并检查以确保它没有崩溃。例如,检查PID是否仍处于活动状态,然后重新启动该进程(如果已经死亡)。
我现在遇到的问题是PID在系统中仍然存在,但应用程序的GUI挂起。我尝试将ProcessBuilder(cmd).start()转换为一个单独的线程,但这似乎没有解决任何问题,正如我希望的那样。
基本上结果是,对于用户来说,程序APPEARS已经崩溃,但是杀死了驱动ProcessBuilder.start()进程的Java进程实际上允许创建的进程恢复其正常行为。这意味着Java应用程序中的某些东西干扰了生成的进程,但此时我完全不知道是什么。 (因此,为什么我尝试将它分成另一个线程,似乎没有解决任何问题)
如果有人有任何意见或想法,请告诉我,因为我不能为我的生活想到如何解决这个问题。
编辑:我不关心从Process创建的I / O流,因此没有采取任何措施来解决这个问题 - 这是否会导致流程本身挂起?
答案 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();
}
}