【问题标题】:How to stop a command being executed after 4-5 seconds through process builder?如何通过进程构建器在 4-5 秒后停止正在执行的命令?
【发布时间】:2016-08-30 19:11:29
【问题描述】:

参考代码:

ProcessBuilder ps4;
Process pr4 = null;

String batchFile3 = new File(path + "/src/example.sh");

ps4 = new ProcessBuilder(batchFile3.getAbsolutePath());

ps4.redirectErrorStream(true);
ps4.directory(new File(path + "/src/"));

pr4 = ps4.start();

BufferedReade readRun = new BufferedReader(new InputStreamReader(pr4.getInputStream()));



if(pr4.waitFor()==0)
{

}

 String line,stre;   

while ((line = readRun.readLine()) != null) {

     System.out.print("-----" + line);

     if (line != null) {

           stre += line;

    }

}
  • 这里我得到了 stre 字符串,它可能是错误或由我正在执行的批处理文件生成的输出。

  • 如果执行和终止批处理文件执行过程需要超过 4-5 秒,我想停止执行批处理文件。

  • 1234563 /li>

【问题讨论】:

  • 这与批处理文件无关。这是一个 Java 问题(也是一个非常基本的编程问题)。

标签: java process timeout processbuilder


【解决方案1】:

据我了解,如果子进程运行时间超过四五秒,您希望停止子进程。这不能直接用ProcessBuilder 完成(您可以看到类中不存在相关方法),但是一旦子流程开始,您就可以轻松实现此行为。

像您在示例代码中那样调用Process.waitFor() 是有问题的,因为它会无限期地阻塞您当前的线程——如果您的进程花费的时间超过五秒,.waitFor() 不会停止它。但是.waitFor() 被重载,它的sibling 接受timeout 参数。

public boolean waitFor(long timeout, TimeUnit unit) throws InterruptedException

如有必要,使当前线程等待,直到此 Process 对象表示的子进程终止,或指定的等待时间过去。

如果需要太长时间,您可以将其与 Process.destroy() 结合使用来停止该过程。例如:

Process process = new ProcessBuilder(command, and, arguments)
    .redirectErrorStream(true)
    .directory(workingDir)
    .start();

process.waitFor(5, TimeUnit.SECONDS);
process.destroy();
process.waitFor(); // wait for the process to terminate

这依赖于 Process.destroy() 在已完成的子进程上调用时是无操作的事实。在 Java 9 之前,这种行为没有被记录,但在实践中一直如此。另一种方法是检查.waitFor() 的返回值,但这会引入TOCTTOU race

Process.destroyForcibly() 呢?一般来说,你不应该调用这个方法(JDK 可能更清楚的另一件事),但是如果一个进程真的挂起,它可能变得有必要。理想情况下,您应该确保您的子流程表现良好,但如果您必须使用.destroyForcibly(),我建议您这样做:

// Option 2
process.waitFor(5, TimeUnit.SECONDS);  // let the process run for 5 seconds
process.destroy();                     // tell the process to stop
process.waitFor(10, TimeUnit.SECONDS); // give it a chance to stop
process.destroyForcibly();             // tell the OS to kill the process
process.waitFor();                     // the process is now dead

这确保了行为不端的进程将被及时终止,同时仍然给正确实施的程序在收到指示后有时间退出。 .destroy().destroyForcibly() 的确切行为是特定于操作系统的,但在 Linux 上我们可以看到 they correspond to SIGTERM and SIGKILL

int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM;
kill(pid, sig);

You should rarely have a need to call .destroyForcibly(),我建议仅在您发现有必要时添加它。

选项 2 在概念上类似于使用 timeout 命令,如下所示:

$ timeout --kill-after=10 5 your_command

在 Java 7 中复制 Process.waitFor(long, TimeUnit) 很容易,default Java 8 implementation 没有什么神奇之处:

public boolean waitFor(long timeout, TimeUnit unit)
    throws InterruptedException
{
    long startTime = System.nanoTime();
    long rem = unit.toNanos(timeout);

    do {
        try {
            exitValue();
            return true;
        } catch(IllegalThreadStateException ex) {
            if (rem > 0)
                Thread.sleep(
                    Math.min(TimeUnit.NANOSECONDS.toMillis(rem) + 1, 100));
        }
        rem = unit.toNanos(timeout) - (System.nanoTime() - startTime);
    } while (rem > 0);
    return false;
} 

【讨论】:

  • 这适用于 java 8 但 java 7 呢?没有带有超时参数的方法waitFor。
  • @GaneshS 您需要使用Thread.sleep()Process.isAlive() 使用轮询循环复制超时行为。
  • 我想出了更好的解决方案,我添加了“timeout -s SIGKILL -k 20s 10s”作为我通过批处理文件执行的命令的前缀。它正在工作。它适用于 Java 7 和 Java 8。
  • java 7 没有 Process.isAlive()。所以不能用 Thread.sleep() 和 Process.isALive() 组合。
  • @GaneshS 抱歉,我正在用手机发表评论。你可以复制isAlive()exitValue()IllegalThreadStateException 表示仍然活着)。
【解决方案2】:

Process 提供并记录在 JavaDoc 中的方法是 Process#destroyForcibly()。然而,强制销毁进程并不总是可行的,进程是否真正被终止在很大程度上取决于操作系统和 JRE 的实现。

有关详细信息,请参阅JavaDoc

【讨论】:

  • 谢谢,我想这会有所帮助。
  • 很少需要打电话给.destroyForcibly();在幕后(在 Linux 上)它发送一个SIGKILL.destroy() 发送更安全的SIGTERM。如果您之前尝试过 .destroy() 进程,并且在宽限期后,该进程继续运行,则应该只使用 .destroyForcibly()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-28
  • 2015-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-09
  • 1970-01-01
相关资源
最近更新 更多