`

java Process waitFor()线程堵死

    博客分类:
  • java
jvm 
阅读更多
在编写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){}  
          }  
   }  

到这里问题的原因也清楚了,问题也被解决了,是不是就结束了。让我们回过头来再分析一下,问题的关键是处在输入流缓冲区那个地方,子进程的产生的输出流没有被JVM及时的读取最后缓冲区满了就卡住了。如果我们能够不让子进程向输入流写入数据,是不是可以解决这个问题。对于这个想法直接去ffmpeg官网查找,最终发现真的可以关闭子进程向窗口写入数据。命令如下:
ffmpeg.exe -loglevel quiet -i 1.mp3 -ab 16k -ar 22050 -acodec libmp3lame r.mp3
稍微分析一下:-acodec 音频流编码方式 -ab 音频流码率(默认是同源文件码率,也需要视codec而定) -ar 音频流采样率(大多数情况下使用44100和48000,分别对应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 Process 使用

    `Process`对象的`waitFor()`方法会阻塞直到进程结束,返回值是进程的退出码: ```java int exitCode = process.waitFor(); System.out.println("Process exited with code: " + exitCode); ``` ### 6. 销毁进程 ...

    java程序设计线程代码

    这个示例展示了如何使用Java的`Runtime`类来启动外部程序(如记事本),并通过`waitFor()`方法等待其完成。 ```java public class RunTime_ProcessDemo { public static void main(String[] args) throws ...

    Java Process Manager

    Java Process Manager是一个基于Java语言开发的工具,它利用JNI(Java Native Interface)技术与Windows 32 SDK(Software Development Kit)进行交互,旨在提供类似操作系统任务管理器的功能,特别是针对Java进程的...

    JAVA教程解析Java的多线程机制

    ### JAVA教程解析Java的多线程机制 #### 进程与应用程序的区别 进程(Process)在多用户、多任务操作系统环境中被定义为应用程序在内存环境中基本执行单元的概念。以Unix操作系统为例,进程是该系统环境中的基本...

    ProcessExplorer(线程cpu占用率工具)

    3. **线程优先级调整**:ProcessExplorer允许用户改变线程的优先级,这对于调试程序或优化性能时控制线程执行顺序非常实用。 4. **内存使用情况**:除了CPU,ProcessExplorer还能显示进程和线程的内存使用情况,...

    Image-Process.rar_Java process-Thread_image process java_java im

    "Image-Process.rar_Java process-Thread_image process java_java im"这个标题暗示了我们将探讨如何在Java中处理图像,特别是涉及线程管理和多线程图像处理。 首先,让我们了解Java中的图像处理基础。Java提供了`...

    java多线程实例

    为了防止多个线程并发访问共享资源导致的数据不一致问题,Java 提供了线程同步机制,包括 `synchronized` 关键字、`wait()`, `notify()` 和 `notifyAll()` 方法。此外,`java.util.concurrent` 包提供了高级同步工具...

    JAVA线程与进程的区别

    JAVA语言中,线程(Thread)和进程(Process)是两个基本概念,它们都是操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。但是,它们之间有着本质的区别。 首先,进程是操作系统...

    java多线程.pdf

    Java多线程是编程领域中一个非常重要的概念,它允许程序中的任务能够被分割成多个独立的执行单元。这种分割使得程序可以同时执行多个任务,从而提高了程序运行的效率和响应速度。在Java中,每个线程都好像拥有自己的...

    java线程与并行(主要讲解java的nio包某些内容)

    ### Java线程与并行详解 #### 一、Java线程基础 在Java中,**线程**是一种轻量级的进程,它允许一个程序同时执行多个任务,从而提高程序的执行效率。Java从1.0版本开始就支持多线程编程,并在后续版本中不断完善。...

    ProcessMonitor线程查看工具

    1. **线程创建与终止**:ProcessMonitor能够捕捉到线程创建和结束的事件,包括创建线程的父进程ID、线程ID、线程优先级等信息。这对于调试多线程程序或排查线程泄露等问题非常有用。 2. **线程上下文切换**:系统...

    Java多线程编程深入详解.docx

    线程通信机制用于实现多线程之间的数据交换,例如wait、notify、notifyAll方法等。 线程初探总结 在本章中,我们探讨了多线程编程的基础知识,包括进程和线程的概念、Java对多线程的支持、第一个多线程程序等。下...

    基于JAVA平台的多线程与并行的资料

    ### 基于JAVA平台的多线程与并行技术详解 #### 一、Java平台上的多线程与并行编程概述 Java平台自诞生以来就内置了对多线程的支持,这使得开发者能够轻松地在Java应用程序中实现并发处理。多线程技术允许在一个...

    Process 线程查看

    标题 "Process 线程查看" 指的是在计算机操作系统中对正在运行的进程及其包含的线程进行监控和管理的技术。这个过程通常涉及到使用特定的工具来获取关于线程的状态、CPU 使用率、内存消耗等信息,以便于调试、性能...

    Java线程检测和数据收集工具

    首先,`jps`(JVM Process Status)是Java虚拟机进程状况工具,它能够列出正在运行的Java进程ID,包括主类名和命令行参数。这个工具非常实用,因为它提供了快速查看哪些Java应用程序正在运行的能力,而无需借助操作...

    Java 软件设计基础:Java线程机制

    在 Java 中,线程(Thread)是进程中某个单个顺序的控制流,也被称为轻量进程(Lightweight Process)。线程是进程中的实体,一个进程可以有多个线程,一个线程必须有一个父进程。线程不拥有系统资源,只有运行必需...

    Java开发中的线程安全选择与Swing

    ### Java开发中的线程安全选择与Swing 在Java开发中,Swing作为构建桌面应用程序图形用户界面(GUI)的主要工具之一,其设计目标是为了提供一个强大、灵活且易于使用的框架。Swing允许开发者轻松创建自定义组件或...

Global site tag (gtag.js) - Google Analytics