[英]How do I launch a completely independent process from a Java program?
我正在開發一個用 Java 編寫的程序,對於某些操作,它使用用戶配置的命令行啟動外部程序。 目前它使用Runtime.exec()
並且不保留Process
引用(啟動的程序是文本編輯器或存檔實用程序,因此不需要系統輸入/輸出/錯誤流)。
但這有一個小問題,即當 Java 程序退出時,它不會真正退出,直到所有啟動的程序都退出。
如果啟動的程序完全獨立於啟動它們的 JVM,我會非常喜歡它。
目標操作系統有多種,Windows、Linux 和 Mac 是最少的,但任何帶有 JVM 的 GUI 系統都是真正需要的(因此實際命令行的用戶可配置性)。
有誰知道如何使啟動的程序完全獨立於 JVM 執行?
編輯回復評論
啟動代碼如下。 代碼可能會啟動位於特定行和列的編輯器,也可能會啟動存檔查看器。 配置的命令行中的引用值被視為 ECMA-262 編碼,並被解碼並去除引號以形成所需的 exec 參數。
發射發生在 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;
}
這似乎只有在從我的 IDE 啟動程序時才會發生。 我正在關閉這個問題,因為該問題僅存在於我的開發環境中; 這在生產中不是問題。 從其中一個答案中的測試程序以及我進行的進一步測試來看,我很滿意這不是任何平台上該程序的任何用戶都會看到的問題。
您的進程之間存在父子關系,您必須打破這種關系。 對於 Windows,您可以嘗試:
Runtime.getRuntime().exec("cmd /c start editor.exe");
對於 Linux,該進程似乎無論如何都可以獨立運行,不需要 nohup。 我用gvim
、 midori
和acroread
試過了。
import java.io.IOException;
public class Exec {
public static void main(String[] args) {
try {
Runtime.getRuntime().exec("/usr/bin/acroread");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Finished");
}
}
我認為以獨立於平台的方式使用Runtime.exec是不可能的。
對於 POSIX 兼容系統:
Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "your command"}).waitFor();
我有一些觀察可以幫助其他面臨類似問題的人。
當您使用 Runtime.getRuntime().exec() 然后忽略返回的 java.lang.Process 句柄時(如原始海報中的代碼),啟動的進程可能會掛起。
我在 Windows 環境中遇到過這個問題,並將問題追溯到 stdout 和 stderr 流。 如果啟動的應用程序正在寫入這些流,並且這些流的緩沖區已滿,則啟動的應用程序在嘗試寫入流時可能會掛起。 解決方法是:
cmd /c <<process>>
執行進程調用(這僅適用於 Windows 環境)。如果您發布重現問題所需的最少代碼的測試部分,這可能會有所幫助。 我在 Windows 和 Linux 系統上測試了以下代碼。
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) throws Exception {
Runtime.getRuntime().exec(args[0]);
}
}
並在 Linux 上進行了以下測試:
java -jar JustForTesting.jar /home/monceaux/Desktop/__TMP/test.sh
其中 test.sh 看起來像:
#!/bin/bash
ping -i 20 localhost
以及Linux上的這個:
java -jar JustForTesting.jar gedit
並在 Windows 上對此進行了測試:
java -jar JustForTesting.jar notepad.exe
所有這些都啟動了他們想要的程序,但 Java 應用程序退出沒有問題。 我有java -version
報告的 Sun JVM 的以下版本:
我還沒有機會在我的 Mac 上進行測試。 也許在您的項目中與其他代碼發生了一些可能不清楚的交互。 您可能想試試這個測試應用程序,看看結果如何。
您想在后台啟動程序,並將其與父程序分開。 我會考慮nohup(1) 。
我嘗試了這里提到的所有內容,但沒有成功。 即使使用 cmd /c start 和重定向流 tu nul,主父 Java 進程也無法退出,直到子線程退出。
對我來說只有一個可靠的解決方案是這樣的:
try {
Runtime.getRuntime().exec("psexec -i cmd /c start cmd.cmd");
}
catch (Exception e) {
// handle it
}
我知道這還不清楚,但是來自 SysInternals 的這個小實用程序非常有用且經過驗證。 這是鏈接。
我懷疑這需要一個實際的流程分支。 基本上,你想要的 C 等價物是:
pid_t id = fork();
if(id == 0)
system(command_line);
問題是你不能在純 Java 中執行 fork() 。 我會做的是:
Thread t = new Thread(new Runnable()
{
public void run()
{
try
{
Runtime.getRuntime().exec(command);
}
catch(IOException e)
{
// Handle error.
e.printStackTrace();
}
}
});
t.start();
這樣 JVM 仍然不會退出,但不會有 GUI,只會保留有限的內存占用。
我能想到的一種方法是使用 Runtime.addShutdownHook 注冊一個終止所有進程的線程(當然,您需要將進程對象保留在某處)。
關閉掛鈎僅在 JVM 退出時調用,因此它應該可以正常工作。
有點黑客但有效。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.