Java ProcessBuilder: результирующие зависания процессов - PullRequest
32 голосов
/ 20 июля 2010

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

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

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

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

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

Ответы [ 8 ]

35 голосов
/ 20 июля 2010

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

12 голосов
/ 20 июля 2010

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

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

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

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

#!/bin/bash

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

То есть: если вас не волнует стандартный вывод и вы все еще хотите протоколировать stderr (не так ли?) в файл ( error.log здесь).

Если вас не интересует stderr, просто перенаправьте его на стандартный вывод:

#!/bin/bash

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

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

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

Другими словами: вышеупомянутый Just Works [TM] и избавление от проблемных "вам нужноиспользовать потоки в том и другом порядке, бла-бла-бла, специфичные для Java бессмысленные ".

11 голосов
/ 15 октября 2012

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

    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();
5 голосов
/ 07 декабря 2011

Просто наткнулся на это после того, как у меня возникла похожая проблема. Соглашаясь с 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/)

2 голосов
/ 11 апреля 2011

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"]
2 голосов
/ 20 июля 2010

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

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

0 голосов
/ 12 октября 2017

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

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

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

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

0 голосов
/ 02 августа 2016

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

...