Java ProcessBuilder: Результирующий Процесс Зависает
Я пытался использовать Java ProcessBuilder для запуска приложения в Linux, которое должно работать "долгосрочно". Способ запуска этой программы-запустить команду (в данном случае я запускаю приложение для воспроизведения мультимедиа), разрешить ей запуск и проверить, не разбилась ли она. Например, проверьте, активен ли еще PID, а затем снова запустите процесс, если он умер.
Проблема, которую я сейчас вижу, заключается в том, что PID остается живым в системе, но графический интерфейс для приложение зависает. Я попробовал перенести ProcessBuilder (cmd).start() в отдельный поток, но это, похоже, ничего не решает, как я и надеялся.
В основном результат заключается в том, что пользователю кажется, что программа разбилась, но убивает процесс Java, который управляет ProcessBuilder.процесс start() фактически позволяет созданному процессу возобновить свое нормальное поведение. Это означает, что что-то в приложении Java вмешивается в порожденный процесс, но На данный момент я понятия не имею, что именно. (Поэтому я попытался разделить его на другой поток, который, казалось, ничего не решал)
Если у кого-то есть какие-либо мысли, пожалуйста, дайте мне знать, так как я не могу в течение всей жизни думать о том, как решить эту проблему.
Edit: я не имею никакого отношения к потоку ввода-вывода, созданному из процесса, и поэтому не предпринял никаких шагов, чтобы справиться с этим-может ли это вызвать зависание в самом процессе?
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
, Если вы хотите снять буферизацию входного канала и канала ошибок.