Java ProcessBuilder:结果进程挂起

2024-10-18 09:00:00
admin
原创
356
摘要:问题描述:我一直在尝试使用 Java 的 ProcessBuilder 在 Linux 中启动一个应该“长期”运行的应用程序。该程序的运行方式是启动一个命令(在本例中,我启动了一个媒体播放应用程序),允许它运行,并检查以确保它没有崩溃。例如,检查 PID 是否仍然处于活动状态,然后重新启动该进程(如果它已经死...

问题描述:

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

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

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

如果有人有任何意见/想法,请告诉我,因为我无论如何也想不出如何解决这个问题。

编辑:我不关心从进程创建的 I/O 流,因此没有采取任何措施来处理它 - 这会导致进程本身挂起吗?


解决方案 1:

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

解决方案 2:

你想要这个诀窍吗?

不要从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 特定的无意义”

解决方案 3:

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

    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();

解决方案 4:

在我遇到类似问题后才偶然发现了这一点。同意 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() 代码)

解决方案 5:

编辑:我不关心从进程创建的 I/O 流,因此没有采取任何措施来处理它 - 这会导致进程本身挂起吗?

如果您不读取进程创建的输出流,那么一旦应用程序的缓冲区已满,应用程序就可能会阻塞。我从未在 Linux 上看到过这种情况(尽管我不是说不会发生),但我在 Windows 上看到过同样的问题。我认为这可能与此有关。

解决方案 6:

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"]

解决方案 7:

另一个解决方案是启动该过程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"
proc.waitFor(); //wait

我在 Windows 和 Linux 上测试了它,并且有效!

解决方案 8:

如果您需要捕获 stdout 和 stderr 并监控进程,那么使用Apache Commons Exec对我有很大帮助。

解决方案 9:

我相信问题出在 Linux 本身的缓冲管道上。

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

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

表示不缓冲输出。如果您想取消缓冲输入和错误管道,和-o0也一样。-i0`-e0`

解决方案 10:

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

假设您有一些与命令相关的错误或响应,而您没有读取这些错误或响应。这将导致应用程序停止,并且 waitFor 会永远等待。一个简单的解决方法是将错误重定向到常规输出。

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

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();
    }
}
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用