Java ProcessBuilder: Результирующий Процесс Зависает


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

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

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

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

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

8 29

8 ответов:

Если процесс пишет в stderr или stdout , а вы его не читаете - он просто "зависнет", блокируя при записи в stdout/err. Либо перенаправить stdout/err в /dev / null с помощью оболочки, либо объединить stdout/err с redirectErrorStream (true) и породить другой поток, считывающий из stdout процесса

Вы хотитетрюк ?

Не запускайте процесс из ProcessBuilder.start () . Не пытайтесь возиться с перенаправлением потока / потреблением из Java (особенно если вы не даете никакого s* * t об этом ;)

Используйте ProcessBuilder.start () для запуска небольшого скрипта оболочки, который поглощает все входные / выходные потоки.

Что-то вроде этого:

#!/bin/bash

nohup $1 >/dev/null 2>error.log &

То есть: если вы не заботитесь о stdout и все еще хотите войти в stderr (не так ли?) в файл (ошибка.log здесь).

Если вы даже не заботитесь о stderr, просто перенаправьте его на stdout:

#!/bin/bash

nohup $1 >/dev/null 2>1 &

И вы вызываете этот крошечный скрипт из Java, давая ему в качестве аргумента имя процесса, который вы хотите запустить.

Если процесс, запущенный в Linux, который перенаправляет оба stdout и stderr в /dev / null, все еще производит что-либо, то у вас есть сломанная, несоответствующая установка Linux ;)

Другими словами: вышеизложенное просто работает [TM] и избавиться от проблематично "вам нужно потреблять потоки в том и в другом порядке bla bla bla Java-specific nonsense" .

Поток, выполняющий процесс, может быть заблокирован, если он не обрабатывает выходные данные. Это можно сделать, создав новый поток, считывающий выходные данные процесса.

    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();

Просто наткнулся на это после того, как у меня была подобная проблема. Соглашаясь с 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() : http://singztechmusings.wordpress.com/2011/06/21/getting-started-with-javas-processbuilder-a-sample-utility-class-to-interact-with-linux-from-java-program/)

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

Если вы не читаете выходные потоки, созданные процессом, то возможно, что приложение заблокирует, как только буферы приложения будут заполнены. Я никогда не видел, как это происходит в Linux (хотя я не говорю, что это не так), но я видел эту точную проблему в Windows. Я думаю, что это скорее всего, родственник.

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"]

В случае, если вам нужно захватить stdout и stderr и контролировать процесс, то использование Apache Commons Exec очень помогло мне.

Я считаю, что проблема заключается в буферизации канала от самого Linux.

Попробуйте использовать stdbuf с вашим исполняемым файлом

new ProcessBuilder().command("/usr/bin/stdbuf","-o0","*executable*","*arguments*");**

-o0 говорит не буферизировать выходные данные. То же самое относится к -i0 и -e0, Если вы хотите снять буферизацию входного канала и канала ошибок.