ProcessBuilder не останавливается

Я пытаюсь декодировать файл mp3 в файл wav, используя класс ProcessBuilder под Linux. По какой-то причине процесс не останавливается, поэтому я должен отменить его вручную.

Может кто подскажет по этому поводу. Я думаю, что приведенный код очень легко воспроизвести:

import java.io.*;

public class Test {
 public static void main(String[] args) {
     try {
         Process lameProcess = new ProcessBuilder("lame", "--decode", "test.mp3", "-").start();
         InputStream is = lameProcess.getInputStream();
         FileOutputStream fileOutput = new FileOutputStream("test.wav");
         DataOutputStream dataOutput = new DataOutputStream(fileOutput);


         byte[] buf = new byte[32 * 1024];
         int nRead = 0;
         int counter = 0;
         while((nRead = is.read(buf)) != -1) {
             dataOutput.write(buf, 0, buf.length);
         }

         is.close();
         fileOutput.close();

     }
     catch (Exception e) {
         e.printStackTrace();
     }
 }
}

Вывод jstack

"main" prio=10 tid=0x0000000002588800 nid=0x247a runnable [0x00007f17e2761000]
   java.lang.Thread.State: RUNNABLE
    at java.io.FileInputStream.readBytes(Native Method)
    at java.io.FileInputStream.read(FileInputStream.java:236)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:235)
    at java.io.BufferedInputStream.read1(BufferedInputStream.java:275)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:334)
    - locked <0x00000000eb5b1660> (a java.io.BufferedInputStream)
    at java.io.FilterInputStream.read(FilterInputStream.java:107)
    at Test.main(Test.java:17)

person rainerhahnekamp    schedule 26.09.2011    source источник
comment
Не следует ли в какой-то момент вызвать метод waitFor() для объекта Process?   -  person biziclop    schedule 27.09.2011
comment
Где он висит? Получите дамп потока с помощью jstack <pid> или kill -3 <pid>.   -  person Philipp Reichart    schedule 27.09.2011
comment
Похоже, вы застряли на read() звонке. Ваш внешний процесс завершился?   -  person Jim Garrison    schedule 27.09.2011
comment
просто добавьте pb.redirecterrorstream(true)   -  person ASHVINI KUMAR    schedule 30.06.2020


Ответы (2)


Вам нужно слить как поток вывода (через getInputStream()), так и поток ошибок (через getErrorStream()) процесса, иначе он может заблокироваться.

Цитируя документацию по процессу:

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

(что относится как к ошибкам, так и к выходным потокам)

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

person prunge    schedule 26.09.2011
comment
Или вы можете использовать ProcessBuilder.redirectErrorStream () чтобы объединить потоки в один, чтобы упростить жизнь. - person Jon7; 27.09.2011
comment
+1. Я советую использовать java.util.concurrent для создания нового потока и передачи заинтересованного InputStream в новый поток для накопления. Вы можете сделать это в одну строку, используя общие ресурсы Apache IO: IOUtils.toString(inputStream, YOUR_CONSOLE_CHARSET); - person dma_k; 27.09.2011
comment
@Jon7 redirectErrorStream(), вероятно, не лучшая идея в этом конкретном случае - выходной поток используется как вывод WAVE, и смешанные сообщения об ошибках могут повредить файл WAV. - person prunge; 27.09.2011

Вам может быть намного проще использовать LAME-оболочку Java, такую ​​как LAMEOnJ. Таким образом, вы избегаете порождения процессов и можете просто взаимодействовать с lame, как если бы это была библиотека Java.

person Jon7    schedule 26.09.2011