Как запустить полностью независимый процесс из 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. Если запущенное приложение пишет эти потоки и буфер для этих потоков заполняются, после чего запущенное приложение может зависнуть при попытке записи в потоки. Решения :

  1. захват дескриптора процесса и постоянно опустошать потоки - но если вы хотите завершить приложение java сразу после запуска процесса, то это не является возможным решением
  2. выполните вызов процесса как "cmd / c " (это только для среды Windows).
  3. суффикс команда процесса и перенаправление потоков 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, поэтому он должен работать нормально.

немного взлом, но эффективный.