如何通过流程构建器在4-5秒后停止执行命令?

时间:2016-05-05 05:33:07

标签: java process timeout processbuilder

参考代码:

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秒来执行批处理文件执行过程。

  • 同样在这种情况下,我应该能够返回程序来处理一个块,只有在处理批处理文件的这种延迟发生时,才会执行该块,否则不应该处理该块。

    < / LI>

2 个答案:

答案 0 :(得分:9)

据我了解,如果子进程运行时间超过四或五秒,则要停止子进程。这不能直接用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;
} 

答案 1 :(得分:0)

Process提供并在JavaDoc中记录的方法是Process#destroyForcibly()。但是,强制销毁进程并且进程是否实际终止并不总是高度依赖于操作系统和JRE实现。

有关详细信息,请参阅JavaDoc