java.lang.Runtime.exec的左膀右臂:流输入和流读取详解
目录
- 什么是java.lang.Runtime
- 什么是java.lang.Process
- 具体做法
- Runtime.exec 陷阱
- IllegalThreadStateException
- 为什么Runtime.exec()挂起
在java.lang.Runtime.exec的使用中,我们经常会用到将重定向命令执行的输入/结果或者将错误信息读取出来.
那么,在使用过程中,我们如何正确的使用呢?
什么是java.lang.Runtime
首先我们要明确一点,什么是Java.lang.Runtime? 我们来看官方[->link<-]的描述:
" Every Java application has a single instance of class Runtime that allows the application to interface with the environment in which the application is running. The current runtime can be obtained from the getRuntime method.
An application cannot create its own instance of this class. "
也就是说,Runtime是每个java-application运行时有且仅有一个的当前实例.允许Application接入当前运行环境.
我们再来看看Runtime的exec()方法:
" Executes the specified command in a separate process. "
这个方法可以让我们在一个进程中执行指定的命令.其返回类型是Process类.
那么我们还需要来看一下Process类:
什么是java.lang.Process
什么是Java.lang.Process? 我们来看官方[->link<-]的描述:
"The class Process provides methods for performing input from the process, performing output to the process, waiting for the process to complete, checking the exit status of the process, and destroying (killing) the process."
也就是说这个类提供控制线程的方法.
我们再来看看Process提供的获取输入流和输出流的方法:
public abstract InputStream getInputStream()
"Returns the input stream connected to the normal output of the subprocess.
The stream obtains data piped from the standard output of the process represented by this Process object."
public abstract OutputStream getOutputStream()
"Returns the output stream connected to the normal input of the subprocess.
Output to the stream is piped into the standard input of the process represented by this Process object."public abstract InputStream getErrorStream()
"Returns the input stream connected to the error output of the subprocess.
The stream obtains data piped from the error output of the process represented by this Process object."
到这里,我们就明白里其中的因果>从exec()返回的Process的对象中调用获取流的方法.从而达到目的!
具体做法
在需要使用exec()去执行命令并获取返回值的时候,具体的做法是什么呢?
仅仅围绕这个思路:"从exec()返回的Process的对象中调用获取流的方法getXStream"
String s = null; Process p = Runtime .getRuntime() .exec( new String[]{"/bin/sh", "-c", "java HelloWorld"},null,dir); BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream())); BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream())); //打印出输出结果 log.info("标准输出命令"); while ((s = stdInput.readLine()) != null) { log.info(s); } log.info("标准错误的输出命令"); while ((s = stdError.readLine()) != null) { log.info(s); }
其中
dir 是我的HelloWorld.class的存放目录,只能这样用,请参照这篇
HelloWorld.java 的内容是Sysotem.out.println打印几行Hello World,此处没有编译,使用时应注意.
到此,大功告成!
Runtime.exec 陷阱
该类java.lang.Runtime具有一个称为的静态方法getRuntime(),该方法检索当前的Java Runtime Environment。这是获得对该Runtime对象的引用的唯一方法。使用该参考,您可以通过调用Runtime类的exec()方法来运行外部程序。开发人员经常调用此方法来启动浏览器,以显示HTML的帮助页面。
该exec()命令有四个重载版本:
public Process exec(String command); public Process exec(String [] cmdArray); public Process exec(String command, String [] envp); public Process exec(String [] cmdArray, String [] envp);
对于这些方法中的每一个,命令(可能还有一组参数)都传递给特定于操作系统的函数调用。随后,这将参考Process返回给Java VM的类来创建特定于操作系统的进程(正在运行的程序)。所述Process类是一个抽象类,因为一个特定的子类Process存在于每个操作系统。
您可以将三个可能的输入参数传递给这些方法:
- 一个字符串,代表要执行的程序和该程序的所有参数
- 字符串数组,用于将程序与其参数分开
- 一组环境变量
以形式传递环境变量name=value。如果exec()对程序及其参数使用单个字符串的版本,请注意,通过StringTokenizer类使用空格作为分隔符来解析该字符串。
IllegalThreadStateException
要执行Java VM外部的进程,我们使用exec()方法。要查看外部进程返回的值,我们exitValue()。
如果外部过程尚未完成,则该exitValue()方法将抛出IllegalThreadStateException;。这就是该程序失败的原因。尽管文档中说明了这一事实,但为什么不能等到此方法可以给出有效答案呢?
对该Process类中可用的方法进行更彻底的研究,就会发现waitFor()可以做到这一点的方法。实际上,waitFor()还会返回退出值,这意味着您将不会使用exitValue()和waitFor()彼此结合,而是会选择一个或另一个。你会使用的唯一可能的时间exitValue(),而不是waitFor()会当你不希望你的程序阻止等待外部过程中可能永远不会完成。与其使用该waitFor()方法,不如将一个被调用的布尔参数waitFor传入该exitValue()方法以确定当前线程是否应等待。布尔值会更有利,因为exitValue()是此方法的更合适的名称,两个方法在不同条件下不必执行相同的功能。这种简单的条件判别是输入参数的领域。
import java.util.*; import java.io.*; public class BadExecJavac { public static void main(String args[]) { try { Runtime rt = Runtime.getRuntime(); Process proc = rt.exec("javac"); int exitVal = proc.exitValue(); System.out.println("Process exitValue: " + exitVal); } catch (Throwable t) { t.printStackTrace(); } } }
import java.util.*; import java.io.*; public class BadExecJavac2 { public static void main(String args[]) { try { Runtime rt = Runtime.getRuntime(); Process proc = rt.exec("javac"); int exitVal = proc.waitFor(); System.out.println("Process exitValue: " + exitVal); } catch (Throwable t) { t.printStackTrace(); } } }
因此,为避免此陷阱,请捕获IllegalThreadStateException或等待该过程完成。
为什么Runtime.exec()挂起
由于某些本机平台仅为标准输入和输出流提供有限的缓冲区大小,因此未能及时写入子流程的输入流或读取子流程的输出流可能导致子流程阻塞,甚至死锁。
现在,让我们关注JDK文档并处理javac过程的输出。当您javac不带任何参数运行时,它会生成一组用法语句,这些用法语句描述了如何运行程序以及所有可用程序选项的含义。知道这将stderr流到流,您可以轻松地编写一个程序以在等待进程退出之前耗尽该流。清单4.3完成了该任务。尽管此方法行之有效,但这不是一个好的通用解决方案。因此,清单4.3的程序被命名为MediocreExecJavac;。它仅提供平庸的解决方案。更好的解决方案将同时清空标准错误流和标准输出流。最好的解决方案是同时清空这些流(稍后再说明)
import java.util.*; import java.io.*; public class MediocreExecJavac { public static void main(String args[]) { try { Runtime rt = Runtime.getRuntime(); Process proc = rt.exec("javac"); InputStream stderr = proc.getErrorStream(); InputStreamReader isr = new InputStreamReader(stderr); BufferedReader br = new BufferedReader(isr); String line = null; System.out.println("<ERROR>"); while ( (line = br.readLine()) != null) System.out.println(line); System.out.println("</ERROR>"); int exitVal = proc.waitFor(); System.out.println("Process exitValue: " + exitVal); } catch (Throwable t) { t.printStackTrace(); } } }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。