参考代码:
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秒,我想停止批处理文件的执行。
在这种情况下,我应该能够返回到程序来处理一个块,该块只有在批处理文件的延迟发生时才会执行,而不应该处理该块。
据我所知,如果子流程运行时间超过四五秒,您需要停止它。这不能直接用ProcessBuilder
来完成(您可以看到类中不存在相关的方法),但一旦子流程开始,您就可以很容易地实现这种行为。
像在示例代码中那样调用Process.waitFor()
是有问题的,因为它会无限期地阻塞当前线程-如果进程花费的时间超过5秒,.waitFor()
不会停止它。但是.waitFor()
重载,它的同级使用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()
时,它是一个非操作。在Java9之前,这种行为没有被记录在案,但在实践中一直都是这样。另一种选择是检查.waitFor()
的返回值,但这将引入TOCTTOU竞赛。
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上,我们可以看到它们对应于SIGTERM
和SIGKILL
:
int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM;
kill(pid, sig);
您应该很少需要调用.destroyForcibly()
,我建议只有在您发现有必要时才添加它。
选项2在概念上类似于使用timeout
命令,如:
$ timeout --kill-after=10 5 your_command
在Java7中复制Process.waitFor(long, TimeUnit)
很容易,默认的Java8实现没有什么神奇之处:
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;
}
Process
提供并在JavaDoc中记录的方法是Process#destroyForcibly()
。但是,强制销毁进程并不总是可能的,进程是否真的被终止在很大程度上取决于操作系统和JRE实现。
有关更多详细信息,请参阅JavaDoc。