java Process的waitFor()
在编写Java程序时,有时候我们需要调用其他的诸如exe,shell这样的程序或脚本。在Java中提供了两种方法来启动其他程序:
(1) 使用Runtime的exec()方法
(2) 使用ProcessBuilder的start()方法
Runtime和ProcessBulider提供了不同的方式来启动程序,设置启动参数、环境变量和工作目录。但是这两种方法都会返回一个用于管理操作系统进程的Process对象。这个对象中的waitFor()是我们今天要讨论的重点。
来说说我遇到的实际情况:我想调用ffmpeg程序来对一首歌曲进行转码,把高音质版本的歌曲转为多种低码率的文件。但是在转码完成之后需要做以下操作:读取文件大小,写入ID3信息等。这时我们就想等转码操作完成之后我们可以知道。
如下这样代码
- Process p = null;
- try {
- p = Runtime.getRuntime().exec("notepad.exe");
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println("我想被打印...");
在notepad.exe被执行的同时,打印也发生了,但是我们想要的是任务完成之后它才被打印。
之后发现在Process类中有一个waitFor()方法可以实现。如下:
- Process p = null;
- try {
- p = Runtime.getRuntime().exec("notepad.exe");
- p.waitFor();
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println("我想被打印...");
这下又出现了这样的现象,必须要等我们把记事本关闭, 打印语句才会被执行。并且你不能手动关闭它那程序就一直不动,程序貌似挂了....
.这是什么情况,想调用个别的程序有这么难吗?让我们来看看waitFor()的说明:
JDK帮助文档上这么说:如有必要,一直要等到由该 Process 对象表示的进程已经终止。如果已终止该子进程,此方法立即返回。但是直接调用这个方法会导致当前线程阻塞,直到退出子进程。对此JDK文档上还有如此解释:因为本地的系统对标准输入和输出所提供的缓冲池有效,所以错误的对标准输出快速的写入何从标准输入快速的读入都有可能造成子进程的所,甚至死锁。好了,
问题的关键在缓冲区这个地方:可执行程序的标准输出比较多,而运行窗口的标准缓冲区不够大,所以发生阻塞。
接着来分析缓冲区,哪来的这个东西,当Runtime对象调用exec(cmd)后,JVM会启动一个子进程,该进程会与JVM进程建立三个管道连接:标准输入,标准输出和标准错误流。
假设该程序不断在向标准输出流和标准错误流写数据,而JVM不读取的话,当缓冲区满之后将无法继续写入数据,最终造成阻塞在waitfor()这里。 知道问题所在,我们解决问题就好办了。查看网上说的方法多数是开两个线程在waitfor()命令之前读出窗口的标准输出缓冲区和标准错误流的内容。代码如下:
- Runtime rt = Runtime.getRuntime();
- String command = "cmd /c ffmpeg -loglevel quiet -i "+srcpath+" -ab "+bitrate+"k -acodec libmp3lame "+desfile;
- try {
- p = rt.exec(command ,null,new File("C:\\ffmpeg-git-670229e-win32-static\\bin"));
- //获取进程的标准输入流
- final InputStream is1 = p.getInputStream();
- //获取进城的错误流
- final InputStream is2 = p.getErrorStream();
- //启动两个线程,一个线程负责读标准输出流,另一个负责读标准错误流
- new Thread() {
- public void run() {
- BufferedReader br1 = new BufferedReader(new InputStreamReader(is1));
- try {
- String line1 = null;
- while ((line1 = br1.readLine()) != null) {
- if (line1 != null){}
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- finally{
- try {
- is1.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }.start();
- new Thread() {
- public void run() {
- BufferedReader br2 = new BufferedReader(new InputStreamReader(is2));
- try {
- String line2 = null ;
- while ((line2 = br2.readLine()) != null ) {
- if (line2 != null){}
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- finally{
- try {
- is2.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }.start();
- p.waitFor();
- p.destroy();
- System.out.println("我想被打印...");
- } catch (Exception e) {
- try{
- p.getErrorStream().close();
- p.getInputStream().close();
- p.getOutputStream().close();
- }
- catch(Exception ee){}
- }
- }
这个方法确实可以解决调用waitFor()方法阻塞无法返回的问题。但是在其中过程中我却发现真正起关键作用的缓冲区是getErrorStream()所对应的那个缓冲区没有被清空,意思就是说其实只要及时读取标准错误流缓冲区的数据程序就不会被block。
- StringBuffer sb = new StringBuffer();
- try {
- Process pro = Runtime.getRuntime().exec(cmdString);
- BufferedReader br = new BufferedReader(new InputStreamReader(pro.getInputStream()), 4096);
- String line = null;
- int i = 0;
- while ((line = br.readLine()) != null) {
- if (0 != i)
- sb.append("\r\n");
- i++;
- sb.append(line);
- }
- } catch (Exception e) {
- sb.append(e.getMessage());
- }
- return sb.toString();
不过这种写法不知道是不是适合所有的情况,网上其他人说的需要开两个线程可能不是没有道理。这个还是具体问题具体对待吧。
这才是我们想要的结果:
- try {
- p = Runtime.getRuntime().exec("cmd /c ffmpeg -loglevel quiet -i D:\\a.mp3 -ab 168k -ar 22050 -acodec libmp3lame D:\\b.mp3",null,
- new File( "C:\\ffmpeg-git-670229e-win32-static\\bin"));
- p.waitFor();
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println("我想被打印...");
最后是自己写的一个简单的操作MP3文件的类
- package com.yearsaaaa.util;
- import java.io.File;
- import java.io.FileInputStream;
- import java.math.BigDecimal;
- import javazoom.jl.decoder.Bitstream;
- import javazoom.jl.decoder.Header;
- /**
- * @className:MP3Util.java
- * @classDescription:
- * @author:MChen
- * @createTime:2012-2-9
- */
- public class MP3Util {
- /**
- * 获取文件大小,以M为单位,保留小数点两位
- */
- public static double getMP3Size(String path)
- {
- File file = new File(path);
- double size = (double)file.length()/(1024*1024);
- size = new BigDecimal(size).setScale(2,BigDecimal.ROUND_UP).doubleValue();
- System.out.println("MP3文件的大小为:"+size);
- return size;
- }
- /**
- * 该方法只能获取mp3格式的歌曲长度
- * 库地址:http://www.javazoom.net/javalayer/javalayer.html
- */
- public static String getMP3Time(String path)
- {
- String songTime = null;
- FileInputStream fis = null;
- Bitstream bt = null;
- File file = new File(path);
- try {
- fis = new FileInputStream(file);
- int b=fis.available();
- bt=new Bitstream(fis);
- Header h=bt.readFrame();
- int time=(int) h.total_ms(b);
- int i=time/1000;
- bt.close();
- fis.close();
- if(i%60 == 0)
- songTime = (i/60+":"+i%60+"0");
- if(i%60 <10)
- songTime = (i/60+":"+"0"+i%60);
- else
- songTime = (i/60+":"+i%60);
- System.out.println("该歌曲的长度为:"+songTime);
- }
- catch (Exception e) {
- try {
- bt.close();
- fis.close();
- } catch (Exception ee) {
- ee.printStackTrace();
- }
- }
- return songTime;
- }
- /**
- * 将源MP3向下转码成低品质的文件
- * @参数: @param srcPath 源地址
- * @参数: @param bitrate 比特率
- * @参数: @param desfile 目标文件
- * @return void
- * @throws
- */
- public static void mp3Transcoding(String srcPath,String bitrate,String desFile)
- {
- //Java调用CMD命令时,不能有空格
- String srcpath = srcPath.replace(" ", "\" \"");
- String desfile = desFile.replace(" ", "\" \"");
- Runtime rt = Runtime.getRuntime();
- String command = "cmd /c ffmpeg -loglevel quiet -i "+srcpath+" -ab "+bitrate+"k -acodec libmp3lame "+desfile;
- System.out.println(command);
- Process p = null;
- try{
- //在Linux下调用是其他写法
- p = rt.exec(command ,null,new File("C:\\ffmpeg-git-670229e-win32-static\\bin"));
- p.waitFor();
- System.out.println("线程返回,转码后的文件大小为:"+desFile.length()+",现在可以做其他操作了,比如重新写入ID3信息。");
- }
- catch(Exception e){
- e.printStackTrace();
- try{
- p.getErrorStream().close();
- p.getInputStream().close();
- p.getOutputStream().close();
- }
- catch(Exception ee){}
- }
- }
- public static void main(String[] args) {
- //String[] str = {"E:\\Kugou\\陈慧娴 - 不羁恋人.mp3","E:\\Kugou\\三寸天堂.mp3","E:\\Tmp\\陈淑桦 - 梦醒时分.mp3","E:\\Tmp\\1.mp3","E:\\Test1\\走天涯、老猫 - 杨望.acc","E:\\Test1\\因为爱情 铃.mp3"};
- String[] str = {"E:\\Kugou\\三寸天堂.mp3"};
- for(String s : str)
- {
- //getMP3Size(s);
- //getMP3Time(s);
- File f = new File(s);
- mp3Transcoding(f.getAbsolutePath(),"64","d:\\chenmiao.mp3");
- }
- }
- }
java Process的waitFor()的更多相关文章
- Memory usage of a Java process java Xms Xmx Xmn
http://www.oracle.com/technetwork/java/javase/memleaks-137499.html 3.1 Meaning of OutOfMemoryError O ...
- Process的Waitfor() 引起代码死锁
Java用process调用c#的exe后,process.waitfor(). exe执行会停在某处.据说是waitfor引起的exe子线程死锁. 先存一个链接 http://yearsaaaa12 ...
- java Process在windows的使用汇总(转)
最常用的是ant(java工程中流行),maven,及通用的exec(只要有shell脚本如.sh,.bat,.exe,.cmd等).而其实前两者不容易出错,后者却遇到了以下问题:Caused by: ...
- Java JPS找不到正在执行的java进程 jps cannot see running java process
最近磁盘进展,把临时目录/tmp给全删了,结果发现jps的输出为空,找不到正在运行的jvm进程. 但是新建的进程没有问题,能够正常查看: [root@node-master ~]# ps -e|gre ...
- 使用Runtime.getRuntime().exec()在java中调用python脚本
举例有一个Python脚本叫test.py,现在想要在Java里调用这个脚本.假定这个test.py里面使用了拓展的包,使得pythoninterpreter之类内嵌的编译器无法使用,那么只能采用ja ...
- Java程序执行Linux命令
Java程序中要执行linux命令主要依赖2个类:Process和Runtime 首先看一下Process类: ProcessBuilder.start() 和 Runtime.exec 方法创建一个 ...
- JAVA 调用exe程序执行对应的文件 (个人用于编译Java文件)
需求: 需要利用Java程序,来调用计算机本身的黑窗口,来将特定的Java文件编译成对应的字节码文件. 实现思路: 通过调用Java的Runtime类,每个 Java 应用程序都有一个 Runtime ...
- Java 调用 shell 脚本详解
这一年的项目中,有大量的场景需要Java 进程调用 Linux的bash shell 脚本实现相关功能. 从之前的项目中拷贝的相关模块和网上的例子来看,有个别的“陷阱”造成调用shell 脚本在某些特 ...
- Java执行shell遇到的各种问题
1.判断子进程是否执行结束 有的时候我们用java调用shell之后,之后的操作要在Process子进程正常执行结束的情况下才可以继续,所以我们需要判断Process进程什么时候终止. Process ...
随机推荐
- [uwp开发]数据绑定那些事(1)
现在是msp候选人,是时候写点技术博客来加分了(实则是个人的心得体会). 注:以下都是个人理解,错误在所难免,欢迎批评指正 以前接触过WPF,只会简单的一些操作,现在在逐渐学习UWP(Universa ...
- 如何设置potplayer播放时总在最前端
1.测试平台:potplayer 64位 2.步骤:在播放器界面上点击右键->选项->基本->最前端方式-选播放视频时在最前端(或是总在最前端) 3.选择:基本->最前端方式- ...
- C++(MFC)编程中遇到的的一些函数
memset void memset( void dest, int c, size_t count ); dest: Pointer to destination c: Character to s ...
- 创建ubuntu软件源
缘起:虚拟机需要安装一些文件,但是直接从外网下载比较慢,而且安装iso包中是有这些软件的,所以创建一个ubuntu apt server ubuntu 12.04, 本机192.168.0.124, ...
- 解决jquery-easyui1.3.3 combobox 多选模式不兼容IE8问题
扩展Array的原型对象,加入indexOf方法 if(!Array.prototype.indexOf){ Array.prototype.indexOf = function(target) ...
- 前端之JavaScript第一天学习(1)-JavaScript 简介
javaScript 是世界上最流行的编程语言. 这门语言可用于 HTML 和 web,更可广泛用于服务器.PC.笔记本电脑.平板电脑和智能手机等设备. JavaScript 是脚本语言 JavaSc ...
- hdu 1853 Cyclic Tour 最小费用最大流
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1853 There are N cities in our country, and M one-way ...
- NYOJ-205 求余数 AC 分类: NYOJ 2014-02-02 12:30 201人阅读 评论(0) 收藏
这题目看一眼以为难度评级出错了,只是一个求余数的题目,,后来才发现,位数小于百万位,,,我还以为是大小小于百万呢,所以借鉴了另一大神的代码, 用大数,重点是同余定理: (a+b)mod m=((a m ...
- 笔直的水管 usaco 背包
背包dp入门,需要滚动数组: #include<iostream> #include<cstdio> #include<string> #include<cs ...
- tcp 多线程与多进程调用close
http://blog.csdn.net/russell_tao/article/details/13092727 大家知道,所谓线程其实就是“轻量级”的进程.创建进程只能是一个进程(父进程)创建另一 ...