Подтвердить что ты не робот

Java ProcessBuilder: результирующие зависания процесса

Я пытаюсь использовать Java ProcessBuilder для запуска приложения в Linux, которое должно работать "долгосрочно". Способ запуска этой программы - запустить команду (в этом случае я запускаю приложение для воспроизведения мультимедиа), разрешаю ему запускать и проверять, чтобы он не разбился. Например, проверьте, активен ли PID, а затем перезапустите процесс, если он умер.

Проблема, с которой я сейчас сталкиваюсь, заключается в том, что PID остается в системе, но GUI для приложения зависает. Я попытался переместить ProcessBuilder (cmd).start() в отдельный поток, но, похоже, он ничего не решает, поскольку я надеялся, что это будет.

В основном результат заключается в том, что для пользователя программа APPEARS разбилась, но убийство процесса Java, который управляет процессом ProcessBuilder.start(), фактически позволяет созданному процессу возобновить нормальное поведение. Это означает, что что-то в приложении Java вмешивается в порожденный процесс, но я совершенно не знаю, что на данный момент. (Поэтому почему я попытался отделить его от другого потока, который, похоже, ничего не разрешил)

Если у кого-то есть какие-либо данные/мысли, пожалуйста, дайте мне знать, поскольку я не могу для жизни меня думать о том, как решить эту проблему.

Изменить: я не беспокоюсь о потоке ввода-вывода, созданного в Process, и поэтому не предпринял никаких шагов для решения этой проблемы - может ли это вызвать зависание в самом Процессе?

4b9b3361

Ответ 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, если вы хотите отключить канал ввода и ошибки.