如何从 Java 程序启动完全独立的进程?
我正在开发一个用 Java 编写的程序,对于某些操作,使用用户配置的命令行启动外部程序。 目前它使用Runtime.exec()
并且不保留Process
引用(启动的程序是文本编辑器或归档实用程序,因此不需要系统在/输出/错误流)。
但这有一个小问题,即当 Java 程序退出时,直到所有启动的程序都退出后,它才真正退出。
如果启动的程序完全独立于启动它们的 JVM,我会非常希望它。
目标操作系统有多种,其中 Windows、Linux 和 Mac 是最少的,但任何带有 JVM 的 GUI 系统都是真正需要的(因此实际命令行的用户可配置性)。
有谁知道如何让启动的程序完全独立于JVM执行?
针对评论进行编辑
启动代码如下。 该代码可以启动位于特定行和列的编辑器,也可以启动存档查看器。 配置的命令行中带引号的值被视为 ECMA-262 编码,并被解码并剥离引号以形成所需的 exec 参数。
发射发生在美国东部时间。
static Throwable launch(String cmd, File fil, int lin, int col) throws Throwable {
String frs[][]={
{ "$FILE$" ,fil.getAbsolutePath().replace('\\','/') },
{ "$LINE$" ,(lin>0 ? Integer.toString(lin) : "") },
{ "$COLUMN$",(col>0 ? Integer.toString(col) : "") },
};
String[] arr; // array of parsed tokens (exec(cmd) does not handle quoted values)
cmd=TextUtil.replace(cmd,frs,true,"$$","$");
arr=(String[])ArrayUtil.removeNulls(TextUtil.stringComponents(cmd,' ',-1,true,true,true));
for(int xa=0; xa<arr.length; xa++) {
if(TextUtil.isQuoted(arr[xa],true)) {
arr[xa]=TextDecode.ecma262(TextUtil.stripQuotes(arr[xa]));
}
}
log.println("Launching: "+cmd);
Runtime.getRuntime().exec(arr);
return null;
}
似乎只有当程序从我的 IDE 启动时才会发生这种情况。 我将关闭这个问题,因为该问题仅存在于我的开发环境中; 这不是生产中的问题。 从答案之一中的测试程序以及我进行的进一步测试来看,我很满意这不是任何平台上的程序的任何用户都会看到的问题。
I am working on a program written in Java which, for some actions, launches external programs using user-configured command lines. Currently it uses Runtime.exec()
and does not retain the Process
reference (the launched programs are either a text editor or archive utility, so no need for the system in/out/err streams).
There is a minor problem with this though, in that when the Java program exits, it doesn't really quit until all the launched programs are exited.
I would greatly prefer it if the launched programs were completely independent of the JVM which launched them.
The target operating system is multiple, with Windows, Linux and Mac being the minimum, but any GUI system with a JVM is really what is desired (hence the user configurability of the actual command lines).
Does anyone know how to make the launched program execute completely independently of the JVM?
Edit in response to a comment
The launch code is as follows. The code may launch an editor positioned at a specific line and column, or it may launch an archive viewer. Quoted values in the configured command line are treated as ECMA-262 encoded, and are decoded and the quotes stripped to form the desired exec parameter.
The launch occurs on the EDT.
static Throwable launch(String cmd, File fil, int lin, int col) throws Throwable {
String frs[][]={
{ "$FILE$" ,fil.getAbsolutePath().replace('\\','/') },
{ "$LINE$" ,(lin>0 ? Integer.toString(lin) : "") },
{ "$COLUMN$",(col>0 ? Integer.toString(col) : "") },
};
String[] arr; // array of parsed tokens (exec(cmd) does not handle quoted values)
cmd=TextUtil.replace(cmd,frs,true,"$","$");
arr=(String[])ArrayUtil.removeNulls(TextUtil.stringComponents(cmd,' ',-1,true,true,true));
for(int xa=0; xa<arr.length; xa++) {
if(TextUtil.isQuoted(arr[xa],true)) {
arr[xa]=TextDecode.ecma262(TextUtil.stripQuotes(arr[xa]));
}
}
log.println("Launching: "+cmd);
Runtime.getRuntime().exec(arr);
return null;
}
This appears to be happening only when the program is launched from my IDE. I am closing this question since the problem exists only in my development environment; it is not a problem in production. From the test program in one of the answers, and further testing I have conducted I am satisfied that it is not a problem that will be seen by any user of the program on any platform.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
您的进程之间存在父子关系,您必须打破它。
对于 Windows,您可以尝试:
对于 Linux,该进程似乎无论如何都独立运行,不需要 nohup。
我用
gvim
、midori
和acroread
尝试过。我认为不可能使用 Runtime.exec 以独立于平台的方式。
对于 POSIX 兼容系统:
There is a parent child relation between your processes and you have to break that.
For Windows you can try:
For Linux the process seem to run detached anyway, no nohup necessary.
I tried it with
gvim
,midori
andacroread
.I think it is not possible to to it with Runtime.exec in a platform independent way.
for POSIX-Compatible system:
我的一些观察可能会帮助其他面临类似问题的人。
当您使用 Runtime.getRuntime().exec() 然后忽略返回的 java.lang.Process 句柄时(如原始海报中的代码所示),启动的进程可能会挂起。
我在 Windows 环境中遇到过这个问题,并将问题追溯到 stdout 和 stderr 流。 如果启动的应用程序正在写入这些流,并且这些流的缓冲区已满,则启动的应用程序在尝试写入流时可能会挂起。 解决方案是:
cmd /c << 执行进程调用;process>>
(仅适用于Windows环境)。I have some observations that may help other people facing similar issue.
When you use Runtime.getRuntime().exec() and then you ignore the java.lang.Process handle you get back (like in the code from original poster), there is a chance that the launched process may hang.
I have faced this issue in Windows environment and traced the problem to the stdout and stderr streams. If the launched application is writing to these streams, and the buffer for these stream fills up then the launched application may appear to hang when it tries to write to the streams. The solutions are:
cmd /c <<process>>
(this is only for Windows environment).如果您发布重现问题所需的最少代码的测试部分,可能会有所帮助。 我在 Windows 和 Linux 系统上测试了以下代码。
并在 Linux 上进行了以下测试:
其中 test.sh 如下所示:
以及在 Linux 上进行了测试:
在 Windows 上进行了测试:
所有这些都启动了其预期的程序,但 Java 应用程序退出时没有任何问题。 我有
java -version
报告的 Sun JVM 的以下版本:我还没有机会在我的 Mac 上进行测试。 也许与项目中其他代码发生的一些交互可能不清楚。 您可能想尝试这个测试应用程序并查看结果。
It may help if you post a test section of minimal code needed to reproduce the problem. I tested the following code on Windows and a Linux system.
And tested with the following on Linux:
where test.sh looks like:
as well as this on Linux:
And tested this on Windows:
All of these launched their intended programs, but the Java application had no problems exiting. I have the following versions of Sun's JVM as reported by
java -version
:I have not had a chance to test on my Mac yet. Perhaps there is some interaction occuring with other code in your project that may not be clear. You may want to try this test app and see what the results are.
您想要在后台启动该程序,并将其与父程序分开。 我会考虑 nohup( 1)。
You want to launch the program in the background, and separate it from the parent. I'd consider nohup(1).
我怀疑这需要一个实际的进程分支。 基本上,您想要的 C 等效项是:
问题是您无法在纯 Java 中执行 fork() 。 我会做的是:
这样 JVM 仍然不会退出,但不会保留 GUI,并且只会保留有限的内存占用。
I suspect this would require a actual process fork. Basically, the C equivalent of what you want is:
The problem is you can't do a fork() in pure Java. What I would do is:
That way the JVM still won't exit, but no GUI and only a limited memory footprint will remain.
我尝试了这里提到的所有内容,但没有成功。 即使使用 cmd /c start 并重定向流 tu nul,主父 Java 进程也无法退出,直到子线程退出。
对我来说只有一个可靠的解决方案是:
我知道这还不清楚,但是 SysInternals 的这个小实用程序非常有帮助并且经过验证。 这里是链接。
I tried everything mentioned here but without success. Main parent Java process can't quit until the quit of subthread even with cmd /c start and redirecting streams tu nul.
Only one reliable solution for me is this:
I know that this is not clear, but this small utility from SysInternals is very helpful and proven. Here is the link.
我能想到的一种方法是使用 Runtime.addShutdownHook 注册一个杀死所有进程的线程(当然,您需要在某处保留进程对象)。
shutdown hook 仅在 JVM 退出时才会被调用,因此它应该可以正常工作。
有点破解但有效。
One way I can think of is to use Runtime.addShutdownHook to register a thread that kills off all the processes (you'd need to retain the process objects somewhere of course).
The shutdown hook is only called when the JVM exits so it should work fine.
A little bit of a hack but effective.