`

Java调用外部程序命令时线程阻塞问题分析

 
阅读更多

    文章参考http://www.qqread.com/java/2010/05/w493489.html

    今天要写个远程重启服务的功能,为了开发速度,暂时定为Java代码+WMIC命令的方法,简单的说,就是利用Java调用本机应用程序的方法。涉及到的 Java类有java.lang包里面的Runtime、Process、ProcessBuilder三个类,以及wmic中重启服务的命令。因为之前 也写过这方面的东西,所以很习惯性的写出了代码:

 

 

Process p = Runtime.getRuntime().exec("wmic ...");
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String tmp = null;
while ((tmp = br.readline()) != null) {
  System.out.println(tmp);
}
int exitValue = p.waitfor();
 

 

  运行,结果发现程序不能退出,Debug发现程序阻塞在br.readline()中了,强制结束程序,发现重启服务的命令正常下下去了,去掉程序中获得标准输出的地方和获得返回结果的地方,命令也能正常下去,而且正常退出。

  为什么程序会阻塞呢?Google了一下,发现了大家的解释,应该也是比较权威的解释吧:每个进程都有自己的标准输入、标准输出、标准错误输出,对于某些 依赖于OS的进程,可能其输出缓冲区很小,如果不能及时的读出(标准输出、标注错误输出),将导致进程不能正常退出。我的程序中标准输出已经读了,显然原因不是这个,难道是错误输出缓冲区中的数据没有读出导致的?带着这个疑问,对程序作了一些更改:

 

ProcessBuilder pb = new ProcessBuilder("wmic",...);
pb.redirectErrorStream(true);
Process p = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String tmp = null;
while ((tmp = br.readline()) != null) {
  System.out.println(tmp);
}
int exitValue = p.waitfor();
  

  编译运行,发现还是有问题,依然还是阻塞。又google了一下,大家的评论大多还是关于标准输出和标准错误输出,那这程序应该是没有问题了。后来在 cmd中敲入wmic的命令,发现wmic命令敲入以后会进入一个自有的提示符中,难道是因为标准输入的问题。后来又google了一下,验证了我的猜 想,果然是因为wmic进程会等待标准输入,而程序中没有处理标准输入的地方,是标准输入阻塞了进程的退出,修改代码:

 

ProcessBuilder pb = new ProcessBuilder("wmic",...);
pb.redirectErrorStream(true);
Process p = pb.start();
p.getOutputStream().close();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String tmp = null;
while ((tmp = br.readline()) != null) {
  System.out.println(tmp);
}
int exitValue = p.waitfor();
   编译运行,程序成功执行。果然是标准输入的原因。

  后来执行的过程中换了一个服务的名称,发现执行失败(能够正常退出,但是返回的结果是“无效动作”),但是同样的命令,在命令行中执行成功,而且直接适用 Runtime.exec()方法中写入整个命令也能够执行成功,难道是ProcessBuilder的错误,ProcessBuilder构造函数有两 个:

  ProcessBuilder(List<String> command)

  ProcessBuilder(String... command)

 

  找到ProcessBuilder的源代码,发现了对List<String>的解析方法:JDK将List中的所有字符串用空格连接,对 list中的每个字符串JDK先判断串中是否包含空格,如果包含空格,用双引号将该字符串引起来,再拼到前面字符串的后面(应该是为了解决路径中包含空格 的问题),可恰好Wmic命令的参数中有一段是name="ServiceName",如果ServiceName中包换空格,JDK就会把 name="service name"的外层加一个双引号,导致wmic不能解析该命令了。

  问题终于全都解决了,耗费了多半天的时间,不过收获总是有的,这里总结一下,在使用Java调用外部命令的时候,一定要注意对标准输出、标准输入和错误输 出的处理。对于一般的命令,只需要将标准输出和错误输出合并,一起读出来或者在另外的线程中读出来,而对于一些特殊的命令,还有处理标准输入。建议即使不 使用标准输入,先close了,总是不会出错了。另外,使用ProcessBuilder时要注意它的空格处理方式是否是你想要的,如果不是,就不能用 ProcessBuilder了,直接使用Runtime.exec()就好了。

    另外,如果子进程Process运行的工作目录与当前主线程的工作目录一相同,则可以用下面两种方法指定子进程Process运行的工作目录。

  ProcessBuilder.directory(new File("filepath"));

  Runtime.getRuntime().exec(command, evn, new File("filepath"));

 

 

分享到:
评论

相关推荐

    Java调用Linux命令

    ### Java调用Linux命令 Java提供了一种强大的机制来执行操作系统级别的任务,其中包括调用Linux命令。实现这一功能的核心是`Runtime`类中的`exec()`方法。这个方法允许Java应用程序创建一个新的进程来执行指定的...

    Java调用相应cmd命令的实例

    在调用CMD命令时,需要注意以下几点: - **命令路径**:如果CMD命令不在系统的PATH环境变量中,需要提供完整路径。 - **错误处理**:`getInputStream()`获取的是命令的标准输出,而`getErrorStream()`获取的是错误...

    调用外部命令.rar

    8. **安全考虑**:调用外部命令时要注意权限和安全问题,避免执行未经验证的用户输入,防止命令注入攻击。例如,不要直接拼接用户输入到命令字符串中,而是使用参数化的方式传递。 9. **异步调用**:在某些情况下,...

    windows下java调用ffmpeg视频处理环境搭建

    接着,了解Java如何调用外部程序。Java提供了`Runtime.getRuntime().exec()`方法或`ProcessBuilder`类来执行系统命令。例如,我们可以使用以下Java代码来执行一个简单的FFmpeg命令: ```java String command = ...

    调用外部程序

    这通常涉及到事件处理和线程管理,确保调用外部程序不会阻塞主线程,导致用户界面冻结。 至于“与外部程序之间的通信”,可以有以下几种方式: 1. **管道(Pipes)**:创建一个管道,使得父进程和子进程之间可以进行...

    ProcessBuilder非阻塞是调用

    ProcessBuilder 非阻塞是调用是指在 Java 中使用 ProcessBuilder 类来执行外部命令或程序时,如何实现非阻塞调用。 在 Java 中,使用 ProcessBuilder 类可以执行外部命令或程序,例如执行系统命令、运行可执行文件...

    java代码中调用linux/unix命令

    有时,单个命令无法满足需求,这时可以编写Shell脚本,然后通过Java调用。例如,使用`bash -c "your_script.sh"`来执行脚本。 在实际开发中,结合Java和Linux/Unix命令可以极大地提高工作效率,尤其是在系统管理和...

    java中调用本地exe文件

    当我们调用一个外部程序时,可能需要捕获它的输出以便进一步处理。这可以通过读取`Process`对象的`InputStream`和`ErrorStream`来实现。 ```java BufferedReader stdInput = new BufferedReader(new ...

    java多线程经典案例

    本案例将深入探讨Java多线程中的关键知识点,包括线程同步、线程通信和线程阻塞。 线程同步是为了防止多个线程同时访问共享资源,导致数据不一致。Java提供了多种同步机制,如synchronized关键字、Lock接口...

    java调用Kettle引用jar包.zip

    Java调用Kettle是将Java程序与Pentaho Kettle(也称为Spoon)集成,以便利用Kettle的强大ETL(提取、转换、加载)能力。Kettle是一款开源的数据集成工具,它允许开发者通过编写XML脚本来执行数据处理任务。在Java...

    java 调用 mplayer

    - 需要注意的是,直接通过命令行调用外部程序可能存在安全风险,比如命令注入。因此,应确保传递给Mplayer的参数是安全的,并限制不必要的用户输入。 - 性能优化方面,避免频繁创建和销毁Mplayer进程,可以尝试...

    Java调用Windows批处理.docx

    3. **错误流和输出流**:调用外部程序时,可能需要处理程序的输出和错误。在示例中,使用`BufferedReader`和`InputStreamReader`读取`Process`对象的`getInputStream()`,从而获取批处理文件的输出。同样的,可以...

    java执行外表命令

    执行外部命令时可能会遇到权限问题、找不到命令等问题,因此需要捕获并处理`IOException`。 在实际开发中,考虑到安全性和可靠性,避免硬编码命令,可以使用配置文件或环境变量来存储命令。此外,对于复杂的命令...

    java调用shell

    这个方法允许Java程序执行外部命令或程序,包括Shell脚本。`Runtime.getRuntime().exec(command)`方法接受一个字符串参数或字符串数组作为命令行指令,创建一个新的进程来执行这些指令,并返回一个`Process`对象,该...

    【原创】java程序cpu占用过高问题分析

    总之,解决Java程序CPU占用过高问题需要多方面的排查和优化,包括但不限于源码优化、线程分析、内存管理、垃圾收集策略调整以及外部资源的优化。通过持续监控、细致的分析和有针对性的改进,可以有效地提高Java应用...

    Java 进程执行外部程序造成阻塞的一种原因

    然而,这样的操作可能会遇到一个常见的问题,即Java进程执行外部程序时发生阻塞,导致程序运行停滞。这个问题可能由多种原因引起,但在这里我们将重点关注一个具体的因素:输出流未被正确处理。 当Java进程执行外部...

    Java调用Python.zip

    当Java调用这个脚本时,它会读取这个输出并处理。 这种方式虽然简单,但有一些限制。例如,Python脚本的执行是异步的,Java程序需要等待Python脚本完成才能继续执行,这可能导致阻塞。此外,这种方式不适合处理大量...

    Java调用microsoft SDK jar

    总结,Java调用微软SDK jar的过程涉及到导入库、理解和使用SDK提供的API,以及处理可能出现的各种问题。通过这种方式,Java开发者可以充分利用微软的强大语音服务,为他们的应用程序添加语音识别和合成功能。

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

    ### Java调用Shell脚本和Python脚本及处理输出结果详解 #### 一、引言 在实际开发过程中,我们经常需要在Java程序中调用Shell脚本或Python脚本来执行一些特定的任务,比如系统管理任务、数据分析等。Java通过`java...

    IO和线程java输入输出 多线程

    Java中的IO(输入/输出)和线程是两个核心概念,它们在开发高效、响应性强的应用程序时起着至关重要的作用。输入/输出处理数据的传输,而多线程则涉及程序的并发执行。 首先,让我们深入理解Java的IO系统。Java.IO...

Global site tag (gtag.js) - Google Analytics