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

Process.waitFor() никогда не возвращается

Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader = 
    new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
4b9b3361

Ответ 1

Существует много причин, по которым waitFor() не возвращается.

Но обычно это сводится к тому, что выполненная команда не уходит.

У этого, опять же, может быть много причин.

Одна общая причина заключается в том, что процесс производит некоторый вывод, и вы не читаете из соответствующих потоков. Это означает, что процесс блокируется, как только буфер заполняется, и ждет, пока ваш процесс продолжит чтение. Ваш процесс, в свою очередь, ждет завершения другого процесса (чего не будет, потому что он ждет вашего процесса,...). Это классическая ситуация взаимоблокировки.

Вам нужно постоянно читать из потока входных процессов, чтобы гарантировать, что он не блокируется.

Там хорошая статья, которая объясняет все подводные камни Runtime.exec() и показывает пути вокруг них под названием "Когда Runtime.exec() не будет" (да, статья с 2000 года, но контент по-прежнему применяется!)

Ответ 2

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

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

ProcessBuilder pb = new ProcessBuilder("tasklist");
pb.redirectErrorStream(true);
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null)
    System.out.println("tasklist: " + line);
process.waitFor();

Ответ 3

Также из Java doc:

java.lang

Процесс класса

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

Невозможно очистить буфер входного потока (какие каналы поступают в выходной поток подпроцесса) из процесса может привести к блокировке подпроцесса.

Попробуйте следующее:

Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();

Ответ 4

Я хотел бы добавить что-то к предыдущим ответам, но поскольку у меня нет комментариев для комментариев, я просто добавлю ответ. Это направлено на пользователей Android, которые программируют на Java.

В сообщении от RollingBoy этот код почти сработал у меня:

Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();

В моем случае waitFor() не освобождался, потому что я выполнял инструкцию без возврата ( "ip adddr flush eth0" ). Легкий способ исправить это - просто обеспечить, чтобы вы всегда возвращали что-то в своем заявлении. Для меня это означало выполнение следующего: "ip adddr flush eth0 & & amcho; echo done". Вы можете читать буфер весь день, но если ничего не вернется, ваш поток никогда не выйдет из строя.

Надеюсь, что это поможет кому-то!

Ответ 5

Как уже упоминалось, вы должны потреблять stderr и stdout.

По сравнению с другими ответами, поскольку Java 1.7 еще проще. Вам больше не нужно создавать темы для чтения stderr и stdout.

Просто используйте ProcessBuilder и используйте методы redirectOutput в сочетании с redirectError или redirectErrorStream.

String directory = "/working/dir";
File out = new File(...); // File to write stdout to
File err = new File(...); // File to write stderr to
ProcessBuilder builder = new ProcessBuilder();
builder.directory(new File(directory));
builder.command(command);
builder.redirectOutput(out); // Redirect stdout to file
if(out == err) { 
  builder.redirectErrorStream(true); // Combine stderr into stdout
} else { 
  builder.redirectError(err); // Redirect stderr to file
}
Process process = builder.start();

Ответ 6

Существует несколько возможностей:

  • Вы не потребляли весь результат процесса stdout.
  • Вы не потребляли весь результат процесса stderr.
  • Процесс ожидает ввода от вас, и вы его не предоставили, или вы не закрыли процесс stdin.
  • Процесс вращается в жесткой петле.

Ответ 7

По той же причине вы также можете использовать inheritIO() для сопоставления консоли Java с внешней консолью приложения, например:

ProcessBuilder pb = new ProcessBuilder(appPath, arguments);

pb.directory(new File(appFile.getParent()));
pb.inheritIO();

Process process = pb.start();
int success = process.waitFor();

Ответ 8

Я думаю, что я наблюдал аналогичную проблему: некоторые процессы начались, казалось, успешно выполнялись, но никогда не завершались. Функция waitFor() ожидала вечно, кроме случаев, когда я убил процесс в диспетчере задач.
Однако все хорошо работало в случаях, когда длина командной строки была 127 символов или короче. Если длинные имена файлов неизбежны, вы можете захотеть использовать переменные среды, которые могут позволить вам сохранить строку командной строки коротким. Вы можете сгенерировать командный файл (используя FileWriter), в котором вы устанавливаете свои переменные окружения перед вызовом программы, которую вы действительно хотите запустить. Содержимое такой партии может выглядеть так:

    set INPUTFILE="C:\Directory 0\Subdirectory 1\AnyFileName"
    set OUTPUTFILE="C:\Directory 2\Subdirectory 3\AnotherFileName"
    set MYPROG="C:\Directory 4\Subdirectory 5\ExecutableFileName.exe"
    %MYPROG% %INPUTFILE% %OUTPUTFILE%

Последний шаг запускает этот командный файл с использованием Runtime.