Как запустить полностью независимый процесс из Java-программы?
Я работаю над программой, написанной на Java, которая,для некоторых действий, запускает внешние программы, используя настроенные пользователем командные строки. В настоящее время он использует Runtime.exec()
и не сохраняет Process
ссылка (запущенные программы являются либо текстовым редактором, либо архивной утилитой, поэтому нет необходимости в потоках системы in/out/err).
есть небольшая проблема с этим, хотя, в том, что, когда программа Java выходит, она действительно не выходит, пока все запущенные программы выходятся.
Я бы предпочел, чтобы запущенные программы были полностью независимы от JVM, который их запустил.
целевая операционная система множественна, с Windows, Linux и Mac является минимальным, но любая система GUI с JVM действительно то, что нужно (следовательно, пользовательская конфигурируемость фактических командных строк).
кто-нибудь знает, как заставить запущенную программу работать полностью независимо от Для 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. Я закрываю этот вопрос, поскольку проблема существует только в моей среде разработки;это не проблема в производстве. Из тестовой программы в одном из ответов и дальнейшего тестирования, которое я провел, я удовлетворен тем, что это не проблема, которую увидит любой пользователь программы на любой платформе.
6 ответов
это может помочь, если вы разместите тестовый раздел минимального кода, необходимого для воспроизведения проблемы. Я протестировал следующий код в 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 не имело проблемы с выходом. У меня есть следующие версии JVM Sun, как сообщает java -version
:
- Windows: 1.6.0_13-b03
- Linux: 1.6.0_10-b33
у меня еще не было возможности протестировать мой Mac. Возможно, в вашем проекте происходит какое-то взаимодействие с другим кодом, которое может быть неясным. Вы можете попробовать это тестовое приложение и посмотреть, каковы результаты.
существует родительское дочернее отношение между вашими процессами, и вы должны его нарушить. Для Windows вы можете попробовать:
Runtime.getRuntime().exec("cmd /c start editor.exe");
для Linux процесс, похоже, работает отдельно в любом случае, нет необходимости.
Я попробовал с 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");
}
}
Я думаю, что это невозможно с во время выполнения.exec независимым от платформы способом.
для POSIX-совместимой системы:
Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "your command"}).waitFor();
хотя этот вопрос закрыт, у меня есть некоторые замечания, которые могут помочь другим людям, сталкивающимся с подобной проблемой.
при использовании среды выполнения.getRuntime().exec (), а затем вы игнорируете java.ленг.Дескриптор процесса вы получаете обратно (как в коде с оригинального плаката), есть вероятность, что запущенный процесс может зависнуть.
Я столкнулся с этой проблемой в среде Windows и проследил проблему до потоков stdout и stderr. Если запущенное приложение пишет эти потоки и буфер для этих потоков заполняются, после чего запущенное приложение может зависнуть при попытке записи в потоки. Решения :
- захват дескриптора процесса и постоянно опустошать потоки - но если вы хотите завершить приложение java сразу после запуска процесса, то это не является возможным решением
- выполните вызов процесса как "cmd / c " (это только для среды Windows).
- суффикс команда процесса и перенаправление потоков stdout и stderr в nul с помощью' команда > nul 2>&1'
вы хотите запустить программу в фоновом режиме, и отделить его от родителей. Я бы подумал nohup (1).
Я подозреваю, что для этого потребуется фактическая вилка процесса. В принципе, эквивалент C того, что вы хотите:
pid_t id = fork();
if(id == 0)
system(command_line);
проблема в том, что вы не можете сделать fork() в чистой Java. Я бы сделал вот что:--3-->
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, поэтому он должен работать нормально.
немного взлом, но эффективный.