`
liyixing1
  • 浏览: 962188 次
  • 性别: Icon_minigender_1
  • 来自: 江西上饶
社区版块
存档分类
最新评论

java.lang.Process调用程序阻塞问题解决

阅读更多

 这两天一直在处理flv视频环境的搭建工作,包括服务器的安装和java中的应用。安装ffmpeg加mencoder倒没有什么大问题,不过还是有一个小问题弄得我郁闷了下,就是在安装amrwb和amrnb的时候出错,错误如下:

 

/usr/bin/wget -N http://www.3gpp.org/ftp/Specs/archive/26_series/26.104/26104-610.zip

--20:41:59--  http://www.3gpp.org/ftp/Specs/archive/26_series/26.104/26104-610.zip

正在解析主机 www.3gpp.org... 212.234.161.21

Connecting to www.3gpp.org|212.234.161.21|:80... 已连接。

已发出 HTTP 请求,正在等待回应... 403 Forbidden

20:42:00 错误 403:Forbidden。

 

make: *** [26104-610.zip] 错误 1

 

    后来仔细看了和问了下公司的网络管理员,才知道我这台测试机器没有开外网,刚开始也想到了是这个问题,可ping的时候却可以ping通,所以就放弃了那个想法,谁知道还真是这问题,结果就好说咯,开通网络后安装就非常顺利了~~~这是题外话了,呵

 

    ffmpeg的环境搭建起来后,在本地进行手动转换没什么问题,通过程序调用(不获取程序反馈结果)也没什么问题。可后来进一步深入的时候,我在提交视频的转换请求的时候,必然要进行数据库的相关操作,比如在转换前更新该视频的状态为正在更新状态,在转换完毕后填如该视频的flv文件路径和图片路径信息,还有flv文件的大小和时间长度等等数据。这就需要在线程中控制ffmpeg进程的状态了,这里我们就需要用到Process这个类了,典型的我们写代码如下:

 

……

Process process = Runtime.getRuntime.exec(cmd); // 执行调用ffmpeg命令

InputStream is = process.getInputStream(); // 获取ffmpeg进程的输出流

BufferedReader br = new Buffered(new InputStreamReader(is)); // 缓冲读入

StringBuilder buf = new StringBuilder(); // 保存ffmpeg的输出结果流

String line = null;

while((line = br.readLine()) != null) buf.append(line); // 循环等待ffmpeg进程结束

System.out.println("ffmpeg输出内容为:" + buf);

……

 

    本来一般都是这样来调用程序并获取进程的输出流的,但是我在windows上执行这样的调用的时候却总是在while那里被堵塞了,结果造成ffmpeg程序在执行了一会后不再执行,这里从官方的参考文档中我们可以看到这是由于缓冲区的问题,由于java进程没有清空ffmpeg程序写到缓冲区的内容,结果导致ffmpeg程序一直在等待。在网上也查找了很多这样的问题,不过说的都是使用单独的线程来进行控制,我也尝试过很多网是所说的方法,可一直没起什么作用。下面就是我的解决方法了,注意到上述代码中的红色部分了么?这里就是关键,我把它改成如下结果就可以正常运行了。

 

InputStream is = process.getErrorStream(); // 获取ffmpeg进程的输出流

 

    注意到没?我把它改成获取错误流这样进程就不会被堵塞了,而我之前一直想的是同样的命令我手动调用的时候可以完成,而java调用却总是完成不了,一直认为是getInputStream的缓冲区没有被清空,不过问题确实是缓冲区的内容没有被清空,但不是getInputStream的,而是getErrorStream的缓冲区,这样问题就得到解决了。所以我们在遇到java调用外部程序而导致线程阻塞的时候,可以考虑使用两个线程来同时清空process获取的两个输入流,如下这段程序:

 

……

Process process = Runtime.getRuntime.exec(command); // 调用外部程序

final InputStream is1 = process.getInputStream();

new Thread(new Runnable() {

    public void run() {

        BufferedReader br = new Buffered(new InputStreamReader(is)); 

        while(br.readLine() != null) ;

    }

}.start(); // 启动单独的线程来清空process.getInputStream()的缓冲区

InputStream is2 = process.getErrorStream();

BufferedReader br2 = new Buffered(new InputStreamReader(is2)); 

StringBuilder buf = new StringBuilder(); // 保存输出结果流

String line = null;

while((line = br.readLine()) != null) buf.append(line); // 循环等待ffmpeg进程结束

System.out.println("输出结果为:" + buf);

……

 

    通过这样我们使用一个线程来读取process.getInputStream()的输出流,使用另外一个线程来获取process.getErrorStream()的输出流,这样我们就可以保证缓冲区得到及时的清空而不担心线程被阻塞了。当然根据需要你也可以保留process.getInputStream()流中的内容,这个就看调用的程序的处理了。我在windows下调用FFmpeg程序进行视频转换的时候就是通过这样来解决线程被堵塞的问题的,呵呵~

 

    所以我觉得像Process.getInputStream和process.getErrorStream两个方法并不是真的就像名称表示的那样,一个是获取被调用程序的正常输出,一个是获取被调用程序的错误流。有时候错误流(即ErrorStream)也是程序的正常输出,因为毕竟java在调用windows和linux下的都是一套这样的程序,所以我们在写程序的时候应该打开思路,我们自己通常认为理所当然的并不一定是正确的呢!

分享到:
评论

相关推荐

    Java调用外部程序命令

    在进行系统集成或自动化脚本编写时,经常需要从Java程序中调用外部程序或命令。这种需求常见于多种场景,例如远程服务管理、批处理操作等。本文将详细介绍如何使用Java来调用外部程序,并解决在这一过程中可能遇到的...

    JAVA Process 使用

    在提供的`Test`文件中,可能包含一个演示了上述概念的Java程序。实际使用时,需要根据具体需求调整`ProcessBuilder`构造的命令和参数。 总结来说,Java中的`Process`和`ProcessBuilder`是实现与操作系统交互的关键...

    java中调用本地exe文件

    在Java中,可以通过`java.lang.Runtime`类或者`java.lang.ProcessBuilder`类来启动外部程序(exe文件)。这两种方式都可以实现同样的功能,但在实际开发过程中,选择哪一种方式取决于具体需求和个人偏好。 - **...

    JAVA如何调用DOS命令.doc

    当执行的程序会产生大量的标准输出时,可能会导致DOS窗口不会自动关闭,进而使得Java程序在`waitFor()`处阻塞。为了解决这个问题,可以通过Java的`BufferedReader`和`InputStreamReader`来读取并处理这些输出。 ...

    JAVA如何调用WINDOWS命令行.doc

    Java提供了`java.lang.Runtime`类和`java.lang.Process`类来帮助开发者在Java程序中执行外部命令。最常用的方法是`Runtime.getRuntime().exec(command)`,该方法用于创建一个新的子进程来执行指定的命令,并返回一个...

    java 调用doc命令

    通过以上方式,我们可以在Java程序中调用`doc`命令,即启动Word并打开指定的文档。同时,`CMDExec`可能指的是使用命令行执行操作的场景,但作为一个具体的库或工具,它并不常见于Java生态中。如果你指的是某种特定的...

    JAVA-DOS-command.rar_DOS java_dos command_java dos_shelf

    总的来说,Java调用DOS命令的能力使得开发者能够在Java程序中集成系统级别的操作,极大地扩展了其功能。熟练掌握这一技术,可以帮助我们更好地将Java与操作系统环境结合,实现更复杂的应用场景。

    Java代码中调用shell脚本和python脚本并获得输出结果(分为小数据量和大数据量).docx

    在实际开发过程中,我们经常需要在Java程序中调用Shell脚本或Python脚本来执行一些特定的任务,比如系统管理任务、数据分析等。Java通过`java.lang.Runtime`类提供了非常方便的方式来启动新的进程,并与之通信。然而...

    java调用shell

    本文将深入探讨如何在Java程序中调用Shell脚本,理解其背后的原理,并提供一系列实用代码示例与应用场景。 ### Java调用Shell的基本原理 Java调用Shell主要依赖于`java.lang.Runtime`类中的`exec()`方法。这个方法...

    java执行可执行文件或批处理

    本文将详细介绍如何在Java程序中执行这些任务,并提供具体的示例来帮助理解。 #### Runtime类简介 `java.lang.Runtime`类提供了运行时环境的信息和操作,包括执行外部进程的能力。`Runtime.getRuntime()`方法用于...

    基于Java的进程通信.zip

    在Java编程语言中,进程通信(Process Communication)是多进程应用程序之间交换信息的方式。Java提供了多种机制来实现进程间的通信,这些机制可以帮助开发者构建复杂的分布式系统。本资料包"基于Java的进程通信.zip...

    进程交互案例

    在Java中,`java.lang.ProcessBuilder`类和`java.lang.Process`接口是进行进程交互的核心工具。`ProcessBuilder`用于创建新的操作系统进程,并允许设置命令行参数、工作目录和环境变量。一旦启动,它将返回一个`...

    java_bat.rar_DEMO

    - 当需要在Java应用程序中执行一些操作系统级别的操作,而这些操作在Java标准库中没有直接支持时,调用批处理文件是一个可行的解决方案。 8. **安全性考虑** - 调用外部程序(包括批处理文件)可能带来安全风险,...

    编程语言java批处理.pdf

    首先,文档中提到了`Runtime`类,它位于`java.lang`包下,是Java程序中进行系统级别的操作时最常用的类之一。`Runtime`类允许Java程序与运行环境交互,比如执行系统命令、获取系统资源使用情况、内存管理等。特别地...

    oracle如何使用java source调用外部程序

    如果在实际操作中遇到类似问题,如Java通过`Runtime`调用外部程序出现阻塞,可以参考相关的解决方法,例如使用`StreamGobbler`类进行异步读取。 总的来说,通过Java Source在Oracle中调用外部程序,可以实现数据库...

    JAVA 管道 EXEC

    Java管道(Pipes)和EXEC在Java...总结来说,Java管道和EXEC提供了强大的能力,允许开发者在Java应用程序中调用外部系统命令并进行数据交换。通过熟练掌握这些工具,开发者可以构建更加灵活和高效的跨平台应用程序。

    Java 执行本地脚本携带多参数

    例如,它不处理标准输入/输出和错误流,这可能导致程序阻塞或数据丢失。为了正确处理这些流,你应该使用`ProcessBuilder`类,它提供了更灵活的控制: ```java List<String> commands = Arrays.asList("/bin/bash", ...

    java_dos_shell_bat

    这些功能允许开发者在Java程序中调用操作系统提供的功能,从而实现自动化任务或者与系统进行更深度的交互。 首先,我们来看如何在Java中运行DOS命令。Java提供了`Runtime`类和`ProcessBuilder`类来执行外部命令。`...

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

    Java线程可以通过继承`java.lang.Thread`类或实现`Runnable`接口来创建。 ##### 创建线程 1. **继承Thread类** - 创建`Thread`类的子类并重写`run()`方法。 - 实例化子类对象,并调用`start()`方法启动线程。 ...

Global site tag (gtag.js) - Google Analytics