Java Process中waitFor()的问题

Java Process中waitFor()的问题

http://yearsaaaa123789.iteye.com/blog/1404865

          在编写Java程序时,有时候我们需要调用其他的诸如exe,shell这样的程序或脚本。在Java中提供了两种方法来启动其他程序:(1) 使用Runtime的exec()方法(2) 使用ProcessBuilder的start()方法 。Runtime和ProcessBulider提供了不同的方式来启动程序,设置启动参数、环境变量和工作目录。但是这两种方法都会返回一个用于管理操作系统进程的Process对象。这个对象中的waitFor()是我们今天要讨论的重点。

      来说说我遇到的实际情况:我想调用ffmpeg程序来对一首歌曲进行转码,把高音质版本的歌曲转为多种低码率的文件。但是在转码完成之后需要做以下操作:读取文件大小,写入ID3信息等。这时我们就想等转码操作完成之后我们可以知道。

如下这样代码

 

Java代码  收藏代码
  1. Process p = null;  
  2. try {  
  3.     p = Runtime.getRuntime().exec("notepad.exe");  
  4. catch (Exception e) {  
  5.     e.printStackTrace();  
  6. }  
  7. System.out.println("我想被打印...");  

 在notepad.exe被执行的同时,打印也发生了,但是我们想要的是任务完成之后它才被打印。


之后发现在Process类中有一个waitFor()方法可以实现。如下:

 

Java代码  收藏代码
  1. Process p = null;  
  2. try {  
  3.     p = Runtime.getRuntime().exec("notepad.exe");  
  4.     p.waitFor();  
  5. catch (Exception e) {  
  6.     e.printStackTrace();  
  7. }  
  8. System.out.println("我想被打印...");  

 这下又出现了这样的现象,必须要等我们把记事本关闭打印语句才会被执行。并且你不碰手动关闭它那程序就一直不动,程序貌似挂了.....这是什么情况,想调用个别的程序有这么难吗?让我们来看看waitFor()的说明:


JDK帮助文档上这么说:如有必要,一直要等到由该 Process 对象表示的进程已经终止。如果已终止该子进程,此方法立即返回。但是直接调用这个方法会导致当前线程阻塞,直到退出子进程。对此JDK文档上还有如此解释:因为本地的系统对标准输入和输出所提供的缓冲池有效,所以错误的对标准输出快速的写入何从标准输入快速的读入都有可能造成子进程的所,甚至死锁。好了,问题的关键在缓冲区这个地方:可执行程序的标准输出比较多,而运行窗口的标准缓冲区不够大,所以发生阻塞。接着来分析缓冲区,哪来的这个东西,当Runtime对象调用exec(cmd)后,JVM会启动一个子进程,该进程会与JVM进程建立三个管道连接:标准输入,标准输出和标准错误流。假设该程序不断在向标准输出流和标准错误流写数据,而JVM不读取的话,当缓冲区满之后将无法继续写入数据,最终造成阻塞在waitfor()这里。 知道问题所在,我们解决问题就好办了。查看网上说的方法多数是开两个线程在waitfor()命令之前读出窗口的标准输出缓冲区和标准错误流的内容。代码如下:

 

Java代码  收藏代码
  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。

 

Java代码  收藏代码
  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();  

 

 不过这种写法不知道是不是适合所有的情况,网上其他人说的需要开两个线程可能不是没有道理。这个还是具体问题具体对待吧。

 

到这里问题的原因也清楚了,问题也被解决了,是不是就结束了。让我们回过头来再分析一下,问题的关键是处在输入流缓冲区那个地方,子进程的产生的输出流没有被JVM及时的读取最后缓冲区满了就卡住了。如果我们能够不让子进程向输入流写入数据,是不是可以解决这个问题。对于这个想法直接去ffmpeg官网查找,最终发现真的可以关闭子进程向窗口写入数据。命令如下:
ffmpeg.exe -loglevel quiet -i 1.mp3 -ab 16k -ar 22050 -acodec libmp3lame r.mp3
稍微分析一下-acodec 音频流编码方式 -ab 音频流码率(默认是同源文件码率,也需要视codec而定) -ar 音频流采样率(大多数情况下使用4410048000,分别对应PAL制式和NTSC制式,根据需要选择),重点就是-loglevel quiet这句 
http://www.ffmpeg.com.cn/index.php/Ffmpeg%E9%80%89%E9%A1%B9%E8%AF%A6%E8%A7%A3

 

这才是我们想要的结果:

 

Java代码  收藏代码
  1. try {  
  2.   p = Runtime.getRuntime().exec("cmd /c ffmpeg -loglevel quiet -i     D:\\a.mp3 -ab 168k -ar 22050 -acodec libmp3lame D:\\b.mp3",null,  
  3.                     new File( "C:\\ffmpeg-git-670229e-win32-static\\bin"));  
  4.   p.waitFor();  
  5. catch (Exception e) {  
  6.     e.printStackTrace();  
  7. }  
  8. System.out.println("我想被打印...");  
 

最后是自己写的一个简单的操作MP3文件的类

 

Java代码  收藏代码
  1. package com.yearsaaaa.util;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.math.BigDecimal;  
  6.   
  7. import javazoom.jl.decoder.Bitstream;  
  8. import javazoom.jl.decoder.Header;  
  9.   
  10. /** 
  11.  * @className:MP3Util.java 
  12.  * @classDescription: 
  13.  * @author:MChen 
  14.  * @createTime:2012-2-9 
  15.  */  
  16. public class MP3Util {  
  17.       
  18.     /** 
  19.      * 获取文件大小,以M为单位,保留小数点两位 
  20.      */  
  21.     public static double getMP3Size(String path)  
  22.     {  
  23.         File file = new File(path);  
  24.         double size = (double)file.length()/(1024*1024);  
  25.         size = new BigDecimal(size).setScale(2,BigDecimal.ROUND_UP).doubleValue();  
  26.         System.out.println("MP3文件的大小为:"+size);  
  27.         return size;  
  28.     }  
  29.       
  30.     /** 
  31.      * 该方法只能获取mp3格式的歌曲长度 
  32.      * 库地址:http://www.javazoom.net/javalayer/javalayer.html 
  33.      */  
  34.     public static String getMP3Time(String path)  
  35.     {  
  36.         String songTime = null;  
  37.         FileInputStream fis = null;  
  38.         Bitstream bt = null;  
  39.         File file = new File(path);  
  40.         try {  
  41.             fis = new FileInputStream(file);  
  42.             int b=fis.available();  
  43.             bt=new Bitstream(fis);  
  44.             Header h=bt.readFrame();  
  45.             int time=(int) h.total_ms(b);  
  46.             int i=time/1000;  
  47.             bt.close();  
  48.             fis.close();  
  49.             if(i%60 == 0)  
  50.                 songTime = (i/60+":"+i%60+"0");  
  51.             if(i%60 <10)  
  52.                 songTime = (i/60+":"+"0"+i%60);  
  53.             else  
  54.                 songTime = (i/60+":"+i%60);  
  55.             System.out.println("该歌曲的长度为:"+songTime);  
  56.         }  
  57.         catch (Exception e) {  
  58.             try {  
  59.                 bt.close();  
  60.                 fis.close();  
  61.             } catch (Exception ee) {  
  62.                 ee.printStackTrace();  
  63.             }  
  64.         }  
  65.         return songTime;  
  66.     }  
  67.       
  68.     /** 
  69.      * 将源MP3向下转码成低品质的文件 
  70.      * @参数: @param srcPath 源地址 
  71.      * @参数: @param bitrate 比特率 
  72.      * @参数: @param desfile 目标文件 
  73.      * @return void    
  74.      * @throws 
  75.      */  
  76.     public static void mp3Transcoding(String srcPath,String bitrate,String desFile)  
  77.     {     
  78.         //Java调用CMD命令时,不能有空格  
  79.         String srcpath = srcPath.replace(" ""\" \"");  
  80.         String desfile = desFile.replace(" ""\" \"");  
  81.         Runtime rt = Runtime.getRuntime();  
  82.         String command = "cmd /c ffmpeg -loglevel quiet -i "+srcpath+" -ab "+bitrate+"k -acodec libmp3lame "+desfile;  
  83.         System.out.println(command);  
  84.         Process p = null;  
  85.         try{  
  86.             //在Linux下调用是其他写法  
  87.             p = rt.exec(command ,null,new File("C:\\ffmpeg-git-670229e-win32-static\\bin"));  
  88.             p.waitFor();  
  89.             System.out.println("线程返回,转码后的文件大小为:"+desFile.length()+",现在可以做其他操作了,比如重新写入ID3信息。");  
  90.         }  
  91.         catch(Exception e){  
  92.             e.printStackTrace();  
  93.             try{  
  94.                 p.getErrorStream().close();  
  95.                 p.getInputStream().close();  
  96.                 p.getOutputStream().close();  
  97.                 }  
  98.             catch(Exception ee){}  
  99.         }  
  100.     }  
  101.       
  102.     public static void main(String[] args) {  
  103.         //String[] str = {"E:\\Kugou\\陈慧娴 - 不羁恋人.mp3","E:\\Kugou\\三寸天堂.mp3","E:\\Tmp\\陈淑桦 - 梦醒时分.mp3","E:\\Tmp\\1.mp3","E:\\Test1\\走天涯、老猫 - 杨望.acc","E:\\Test1\\因为爱情 铃.mp3"};  
  104.         String[] str = {"E:\\Kugou\\三寸天堂.mp3"};  
  105.         for(String s : str)  
  106.         {  
  107.             //getMP3Size(s);  
  108.             //getMP3Time(s);  
  109.             File f = new File(s);  
  110.             mp3Transcoding(f.getAbsolutePath(),"64","d:\\chenmiao.mp3");  
  111.         }  
  112.     }  
  113. }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/353720.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

应用程序模块和实体缓存

任何具有ADF业务组件基础知识的ADF开发人员都应该熟悉下图&#xff1a; 它代表运行时ADF业务组件的核心构建块。 有一个包含视图对象实例的根应用程序模块实例。 视图对象实例可能由存储在实体集合或换句话说就是实体缓存中的实体对象备份。 根应用程序模块可能还包含嵌套的应…

kubernetes-dashboard(1.8.3)部署与踩坑

Kubernetes Dashboard 是一个管理Kubernetes集群的全功能Web界面&#xff0c;旨在以UI的方式完全替代命令行工具&#xff08;kubectl 等&#xff09;。 目录 部署创建用户集成Heapster访问 kubectl proxyNodePortAPI ServerIngress部署 Dashboard需要用到k8s.gcr.io/kubernetes…

oracle线程阻塞_Oracle Service Bus –线程阻塞案例研究

oracle线程阻塞本案例研究描述了在AIX 6.1和IBM Java VM 1.6上运行的Oracle Service Bus 11g遇到的线程阻塞问题的完整根本原因分析过程。 本文也是您提高线程转储分析技能的绝佳机会&#xff0c;我强烈建议您学习并正确理解以下分析方法。 它还将展示正确数据收集的重要性&…

Activiti中的安全脚本如何工作

最近的Activiti 5.21.0版本的突出特点之一是“安全脚本”。 Activiti用户指南中详细介绍了启用和使用此功能的方法 。 在这篇文章中&#xff0c;我将向您展示我们如何实现其最终实现以及它在幕后所做的事情。 当然&#xff0c;由于这是我通常的签名风格&#xff0c;因此我们还将…

使用准现网的数据,使用本地的样式脚本,本地调试准现网页面(PC适用)

原理&#xff1a; 本地逻辑&#xff0c;重新渲染 步骤&#xff1a; 1.安装插件&#xff1a;Tampermonkey 度盘&#xff1a;https://pan.baidu.com/s/1bpBVVT9 2.设置&#xff1a; 点击插件-->仪表盘 添加脚本 将此文本文档中的脚本复制到脚本编辑框处&#xff0c;并CtrlS保存…

FDATOOL设计滤波器

FDATOOL设计滤波器 分类&#xff1a; 数字信号处理 2006-04-20 11:251. 在Matlab中键入fdatool运行Filter Design and Analysis Tool。具体使用请参见Matlab Help中的Signal Processing Toolbox->FDATool。 2. 在fdatool工具中应该注意的几个问题&#xff1a;(a)Fstop&#…

大例外背后的真相

异常可能是最被滥用的Java语言功能。 这就是为什么 让我们打破一些神话。 没有牙仙子。 圣诞老人不是真实的。 TODO评论。 finalfinalversion-final.pdf。 无皂肥皂。 而且…例外实际上是例外。 后者可能需要更多说服力&#xff0c;但我们可以帮助您。 在这篇文章中&#xff…

MATLAB里面的filter和filtfilt的C语言源代码

MATLAB里面的filter和filtfilt的C语言源代码 嗯&#xff0c;算法非常简单&#xff0c;就是网上搜不到C代码实现。filter是个很万能的数字滤波器函数&#xff0c;只要有滤波器的差分方程系数&#xff0c;IIR呀FIR呀都能通过它实现。在MATLAB里面&#xff0c;filter最常用的格式是…

20172302『Java程序设计』课程 结对编程练习_四则运算第二周阶段总结

一.结对对象 姓名&#xff1a;周亚杰学号&#xff1a;20172302担任角色&#xff1a;驾驶员&#xff08;周亚杰&#xff09;伙伴第二周博客地址二.本周内容 (一)继续编写上周未完成代码 1.本周继续编写代码&#xff0c;使代码支持分数类计算 2.相关过程截图 a.下图是上周编写的生…

实践中的弹性基础架构

几周前&#xff0c;我获得了一个难得的机会&#xff0c;可以在基础设施领域弄脏双手。 在JVM内部的深入了解下&#xff0c;我每天的工作经历发生了有趣的变化&#xff0c;我想与您分享动机和成果。 希望它可以启发类似的问题类别。 背景 我将从解释需要解决方案的上下文开始。…

notepad++插件实现json、xml格式化

notepad比较出色的免费的数据编辑、格式化工具。。。 现在json、xml文件很流行、格式化也是必须的&#xff0c;方便查看关键信息&#xff01; 01、下载notepad及相关插件 npp_7.5.5-x86&#xff1a; https://files.cnblogs.com/files/xiaochina/npp_7.5.5-x86.zip npp-json:…

ActiveMQ 5.x中的消息持久性

我被问了很多关于ActiveMQ如何存储消息&#xff08;或在某些情况下不存储&#xff09;的基本知识。 这是它的高级解释。 注意&#xff0c;上下文在JMS中。 如果您使用ActiveMQ的非JMS客户端&#xff08;即STOMP&#xff0c;AMQP&#xff0c;MQTT等&#xff09;&#xff0c;则在…

一个select元素自定义设计的新思路:appearance: none之后利用符号制造小箭头

最近工作时解决了一个前端小问题&#xff08;如下图所示&#xff09;&#xff1a;在Safari中&#xff0c;select的控件之上有不和谐的灰色部分。 刚开始时我以为是backgrand或是border设置不当之类产生的问题&#xff0c;在搜索了很久之后终于找到了问题所在&#xff1a;这个灰…

调整HashMap的大小:未来的危险

最近&#xff0c;我偶然发现了一个错误&#xff0c;该错误是由于多个线程对java.util.HashMap的使用不当引起的。 该错误是抽象泄漏的一个很好的例子。 只有了解数据结构的实现级别详细信息&#xff0c;才能帮助我解决当前的问题。 因此&#xff0c;我希望分享我所面临的问题将…

别的程序员是怎么读你的简历的

别的程序员是怎么读你的简历的 2009年11月9日 陈皓 下面这个图片来源国外&#xff0c;是一个关于程序员面试时的简历&#xff0c;被人事部门和程序员本身评审的角度不同的图片。当然&#xff0c;这是一个从国外面试的视角制作的图片&#xff0c;不过&#xff0c;可以看出&#…

Zabbix linux agent 安装

系统&#xff1a;Linux Centos 7.3 x64 服务&#xff1a;Zabbix_agent 3.0.16 一.安装Zabbix_agent 服务 1.安装zabbix 3.0 yum源 rpm -ivh http://repo.zabbix.com/zabbix/3.0/rhel/7/x86_64/zabbix-release-3.0-1.el7.noarch.rpm 2.安装Zabbix_agent yum install zabbix-agen…

直接在apk中添加资源的研究

原文 http://blog.votzone.com/2018/05/12/apk-merge.html 之前接手过一个sdk的开发工作&#xff0c;在开发过程中有一个很重要的点就是尽量使用代码来创建控件&#xff0c;资源文件最好放到assets目录下&#xff0c;如果必须使用res资源&#xff0c;需要通过 getResources().g…

JavaFX实际应用程序:SkedPal

“真实世界的应用程序”系列中的一个新条目。 这次是SkedPal &#xff0c;这是一个用于智能管理忙人生活的应用程序。 我一直在咨询SkedPal团队有关JavaFX的事宜&#xff0c;并且在他们决定开始使用我的CalendarFX框架来满足他们的日历要求时&#xff0c;我也在咨询他们。 在下…

chromium之histogram.h

histogram不知道是干啥的 // Histogram is an object that aggregates statistics, and can summarize them in // various forms, including ASCII graphical, HTML, and numerically (as a // vector of numbers corresponding to each of the aggregating buckets). google翻…

viewobject_只读ViewObject和声明性SQL模式

viewobject介绍 声明式SQL模式被认为是基于实体的视图对象的最有价值的优点之一。 在这种模式下&#xff0c;根据UI中显示的属性在运行时生成VOSQL。 例如&#xff0c;如果某个页面包含一个只有两列EmployeeId和FirstName的表&#xff0c;则查询将生成为“从Employees中选择Emp…