Java 中的进程生成器和进程 - 如何执行超时进程:?
我需要在 java 中执行具有特定超时的外部批处理文件。这意味着如果批处理执行时间超过指定的超时时间,我需要取消执行。
这是我编写的示例代码:
public static void main(String[] args) throws IOException, InterruptedException {
ProcessBuilder p = new ProcessBuilder("c:\\wait.bat", "25"); // batch file execution will take 25 seconds.
final long l = System.currentTimeMillis();
System.out.println("starting..." + (System.currentTimeMillis() - l));
final Process command = p.start();
System.out.println("started..." + (System.currentTimeMillis() - l));
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
command.destroy();
}
}, 5000); // it will kill the process after 5 seconds (if it's not finished yet).
int i = command.waitFor();
t.cancel();
System.out.println("done..." + (System.currentTimeMillis() - l));
System.out.println("result : " + i);
System.out.println("Really Done..." + (System.currentTimeMillis() - l));
}
批处理文件“wait.bat”是这样的:
@echo off
echo starting the process...
@ping 127.0.0.1 -n 2 -w 1000 > nul
@ping 127.0.0.1 -n %1% -w 1000> nul
echo process finished succesfully
@echo on
正如您在代码中看到的,批处理文件将需要 25 秒才能完成(主方法中的第一行),并且计时器将销毁5 秒后发出命令。
这是我的代码的输出:
starting...0
started...0
done...5000
result : 1
Really Done...5000
BUILD SUCCESSFUL (total time: 25 seconds)
正如您在输出中看到的,最后一行(“Really Done...”)在第 5 秒内执行,但应用程序在 25 秒后完成。
我的问题是:即使我在计时器中调用了 destroy 方法,为什么 jvm 仍然等待进程完成?
I need to execute an external batch file in java with a specific timeout. which means that if the batch execution take longer than specified timeout, i need to cancel the execution.
here is a sample code that i wrote:
public static void main(String[] args) throws IOException, InterruptedException {
ProcessBuilder p = new ProcessBuilder("c:\\wait.bat", "25"); // batch file execution will take 25 seconds.
final long l = System.currentTimeMillis();
System.out.println("starting..." + (System.currentTimeMillis() - l));
final Process command = p.start();
System.out.println("started..." + (System.currentTimeMillis() - l));
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
command.destroy();
}
}, 5000); // it will kill the process after 5 seconds (if it's not finished yet).
int i = command.waitFor();
t.cancel();
System.out.println("done..." + (System.currentTimeMillis() - l));
System.out.println("result : " + i);
System.out.println("Really Done..." + (System.currentTimeMillis() - l));
}
the batch file "wait.bat" is something like this:
@echo off
echo starting the process...
@ping 127.0.0.1 -n 2 -w 1000 > nul
@ping 127.0.0.1 -n %1% -w 1000> nul
echo process finished succesfully
@echo on
As you see in the code, batch file will take 25 seconds to finish (first line in main method) and the Timer will destroy the command after 5 seconds.
here is the output of my code:
starting...0
started...0
done...5000
result : 1
Really Done...5000
BUILD SUCCESSFUL (total time: 25 seconds)
as you see in output, the last line ("Really Done...") is executed in 5th second but the application is finished after 25 seconds.
my question is that : even though i called the destroy method in my timer, why jvm still waiting for the process to be finished ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是 Java 的 Process.destroy() 中的一个 bug在 Windows 上的实现。问题是批处理脚本(或其执行 shell)被终止,但没有终止其自己的子进程(此处的 ping)。因此,在
.destroy()
之后以及.waitFor()
之后,ping 仍在运行。但不知何故,虚拟机仍然等待 ping 完成后再自行完成。看来您在 Java 方面无能为力,无法真正可靠地终止 ping。
您可能会考虑使用
start
(在批处理脚本中或外部)将 ping 作为单独的进程调用。(另请参阅之前的讨论。)
或者换成类unix操作系统。
It is a bug in Java's
Process.destroy()
implementation on Windows. The problem is that the batch-script (or its executing shell) is killed, but does not kill its own child processes (the ping here). Thus, ping is still running after the.destroy()
, and also after the.waitFor()
. But somehow the VM still waits for the ping to finish before finishing itself.It seems there is nothing you can do here from the Java side to really kill the ping reliably.
You may think about using
start
(in your batch script or outside) to invoke your ping as a separate process.(See also this previous discussion.)
Or change to a unix-like operation system.
如果您使用 Unix/Linux,则编写一个包装器 bash shell 脚本以通过超时中断外部命令,然后从 Java 调用该包装器。
包装器脚本看起来像
您可以通过检查脚本退出代码来检测超时是否过期,如果超时,则为 124
请参阅
If you use Unix/Linux, then write a wrapper bash shell script to interrupt an external command by timeout, then call the wrapper from Java.
The wrapper script looks like
You can detect if timeout expired by checking the script exit code which is 124 in case of timeout
See
我可能是计时器的取消方法有问题。尝试将计时器作为守护线程启动。
I could be a problem with the cancel method of the timer. try to start the timer as daemon thread.