Java ProcessBuilder: результирующие зависания процесса
Я пытаюсь использовать Java ProcessBuilder для запуска приложения в Linux, которое должно работать "долгосрочно". Способ запуска этой программы - запустить команду (в этом случае я запускаю приложение для воспроизведения мультимедиа), разрешаю ему запускать и проверять, чтобы он не разбился. Например, проверьте, активен ли PID, а затем перезапустите процесс, если он умер.
Проблема, с которой я сейчас сталкиваюсь, заключается в том, что PID остается в системе, но GUI для приложения зависает. Я попытался переместить ProcessBuilder (cmd).start() в отдельный поток, но, похоже, он ничего не решает, поскольку я надеялся, что это будет.
В основном результат заключается в том, что для пользователя программа APPEARS разбилась, но убийство процесса Java, который управляет процессом ProcessBuilder.start(), фактически позволяет созданному процессу возобновить нормальное поведение. Это означает, что что-то в приложении Java вмешивается в порожденный процесс, но я совершенно не знаю, что на данный момент. (Поэтому почему я попытался отделить его от другого потока, который, похоже, ничего не разрешил)
Если у кого-то есть какие-либо данные/мысли, пожалуйста, дайте мне знать, поскольку я не могу для жизни меня думать о том, как решить эту проблему.
Изменить: я не беспокоюсь о потоке ввода-вывода, созданного в Process, и поэтому не предпринял никаких шагов для решения этой проблемы - может ли это вызвать зависание в самом Процессе?
Ответы
Ответ 1
Если процесс пишет в stderr
или stdout
, а вы его не читаете - он просто "зависнет", блокируясь при записи в stdout/err
. Либо перенаправьте stdout/err
в /dev/null
с помощью оболочки, либо объедините stdout/err
с redirectErrorStream (true) и создайте другой поток, который читает из stdout
процесса
Ответ 2
Вам нужен трюк?
Не запускайте свой процесс из ProcessBuilder.start(). Не пытайтесь испортить перенаправление/потребление потока из Java (особенно если вы не указали об этом;)
Используйте ProcessBuilder.start(), чтобы запустить небольшую оболочку script, которая поглощает все входные/выходные потоки.
Что-то вроде этого:
#!/bin/bash
nohup $1 >/dev/null 2>error.log &
То есть: если вы не заботитесь о stdout и все еще хотите записать stderr (вы?) в файл (здесь error.log).
Если вы даже не заботитесь о stderr, просто перенаправьте его на stdout:
#!/bin/bash
nohup $1 >/dev/null 2>1 &
И вы называете это маленьким script с Java, давая ему в качестве аргумента имя процесса, который вы хотите запустить.
Если процесс, запущенный в Linux, который перенаправляет как stdout, так и stderr на /dev/null, все еще производит что-либо, тогда у вас есть сломанная, несовместимая, Linux install;)
Другими словами: приведенные выше Just Works [TM] и избавиться от проблемных "вам нужно потреблять потоки в этом и этом порядке bla bla bla, специфичные для Java, не имеющие смысла".
Ответ 3
Поток, выполняющий этот процесс, может блокироваться, если он не обрабатывает вывод. Это можно сделать, создав новый поток, который считывает выходные данные процесса.
final ProcessBuilder builder = new ProcessBuilder("script")
.redirectErrorStream(true)
.directory(workDirectory);
final Process process = builder.start();
final StringWriter writer = new StringWriter();
new Thread(new Runnable() {
public void run() {
IOUtils.copy(process.getInputStream(), writer);
}
}).start();
final int exitValue = process.waitFor();
final String processOutput = writer.toString();
Ответ 4
Просто наткнулся на это после того, как у меня возникла похожая проблема. Соглашаясь с nos, вам нужно обработать вывод. У меня было что-то вроде этого:
ProcessBuilder myProc2 = new ProcessBuilder(command);
final Process process = myProc2.start();
и он работал отлично. Порожденный процесс даже выводил некоторый вывод, но не очень. Когда я начал выводить намного больше, оказалось, что мой процесс больше не запускается. Я обновил это:
ProcessBuilder myProc2 = new ProcessBuilder(command);
myProc2.redirectErrorStream(true);
final Process process = myProc2.start();
InputStream myIS = process.getInputStream();
String tempOut = convertStreamToStr(myIS);
и он снова начал работать. (Обратитесь к этой ссылке для получения кода convertStreamToStr())
Ответ 5
Изменить: я не беспокоюсь о потоке ввода-вывода, созданного в Process, и поэтому не предпринял никаких шагов для решения этой проблемы - может ли это вызвать зависание в самом Процессе?
Если вы не читаете выходные потоки, созданные процессом, возможно, приложение будет блокироваться после заполнения буферов приложений. Я никогда не видел, чтобы это происходило в Linux (хотя я не говорю, что это не так), но я видел эту точную проблему в Windows. Я думаю, что это, вероятно, связано.
Ответ 6
JDK7 будет иметь встроенную поддержку перенаправления ввода/вывода подпроцесса:
http://download.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html
Тем временем, если вы действительно хотите отказаться от stdout/stderr, лучше всего (в Linux) вызывать ProcessBuilder по команде, которая выглядит так:
["/bin/bash", "-c", "exec YOUR_COMMAND_HERE >/dev/null 2>&1"]
Ответ 7
Если вам нужно захватить stdout и stderr и следить за процессом, используйте Apache Commons Exec помогли мне много.
Ответ 8
Я считаю, что проблема заключается в буферизации из самого Linux.
Попробуйте использовать stdbuf
с вашим исполняемым файлом
new ProcessBuilder().command("/usr/bin/stdbuf","-o0","*executable*","*arguments*");**
-o0
говорит, что не буферизовать вывод.
То же самое относится к -i0
и -e0
, если вы хотите отключить канал ввода и ошибки.