摘录自:http://lelglin.iteye.com/blog/1487351
JDK帮助文档上说:如有必要,一直要等到由该 Process 对象表示的进程已经终止。如果已终止该子进程,此方法立即返回。但是直接调用这个方法会导致当前线程阻塞,直到退出子进程。对此JDK文档上还有如此解释:因为本地的系统对标准输入和输出所提供的缓冲池有效,所以错误的对标准输出快速的写入何从标准输入快速的读入都有可能造成子进程的所,甚至死锁。好了,
问题的关键在缓冲区这个地方:可执行程序的标准输出比较多,而运行窗口的标准缓冲区不够大,所以发生阻塞。
接着来分析缓冲区,哪来的这个东西,当Runtime对象调用exec(cmd)后,JVM会启动一个子进程,该进程会与JVM进程建立三个管道连接:标准输入,标准输出和标准错误流。
假设该程序不断在向标准输出流和标准错误流写数据,而JVM不读取的话,当缓冲区满之后将无法继续写入数据,最终造成阻塞在waitfor()这里。 知道问题所在,我们解决问题就好办了。查看网上说的方法多数是开两个线程在waitfor()命令之前读出窗口的标准输出缓冲区和标准错误流的内容。代码如下:
1 Runtime rt = Runtime.getRuntime(); 2 String command = "cmd /c ffmpeg -loglevel quiet -i "+srcpath+" -ab "+bitrate+"k -acodec libmp3lame "+desfile; 3 try { 4 p = rt.exec(command ,null,new File("C:\\ffmpeg-git-670229e-win32-static\\bin")); 5 //获取进程的标准输入流 6 final InputStream is1 = p.getInputStream(); 7 //获取进城的错误流 8 final InputStream is2 = p.getErrorStream(); 9 //启动两个线程,一个线程负责读标准输出流,另一个负责读标准错误流 10 new Thread() { 11 public void run() { 12 BufferedReader br1 = new BufferedReader(new InputStreamReader(is1)); 13 try { 14 String line1 = null; 15 while ((line1 = br1.readLine()) != null) { 16 if (line1 != null){} 17 } 18 } catch (IOException e) { 19 e.printStackTrace(); 20 } 21 finally{ 22 try { 23 is1.close(); 24 } catch (IOException e) { 25 e.printStackTrace(); 26 } 27 } 28 } 29 }.start(); 30 31 new Thread() { 32 public void run() { 33 BufferedReader br2 = new BufferedReader(new InputStreamReader(is2)); 34 try { 35 String line2 = null ; 36 while ((line2 = br2.readLine()) != null ) { 37 if (line2 != null){} 38 } 39 } catch (IOException e) { 40 e.printStackTrace(); 41 } 42 finally{ 43 try { 44 is2.close(); 45 } catch (IOException e) { 46 e.printStackTrace(); 47 } 48 } 49 } 50 }.start(); 51 52 p.waitFor(); 53 p.destroy(); 54 System.out.println("我想被打印..."); 55 } catch (Exception e) { 56 try{ 57 p.getErrorStream().close(); 58 p.getInputStream().close(); 59 p.getOutputStream().close(); 60 } 61 catch(Exception ee){} 62 } 63 }
这个方法确实可以解决调用waitFor()方法阻塞无法返回的问题。但是在其中过程中我却发现真正起关键作用的缓冲区是getErrorStream()所对应的那个缓冲区没有被清空,意思就是说其实只要及时读取标准错误流缓冲区的数据程序就不会被block。
1 StringBuffer sb = new StringBuffer(); 2 try { 3 Process pro = Runtime.getRuntime().exec(cmdString); 4 BufferedReader br = new BufferedReader(new InputStreamReader(pro.getInputStream()), 4096); 5 String line = null; 6 int i = 0; 7 while ((line = br.readLine()) != null) { 8 if (0 != i) 9 sb.append("\r\n"); 10 i++; 11 sb.append(line); 12 } 13 } catch (Exception e) { 14 sb.append(e.getMessage()); 15 } 16 return sb.toString();
不过这种写法不知道是不是适合所有的情况,网上其他人说的需要开两个线程可能不是没有道理。这个还是具体问题具体对待吧。