关于java runtimeexec 的讲解.docx
- 文档编号:10804273
- 上传时间:2023-02-23
- 格式:DOCX
- 页数:19
- 大小:21.11KB
关于java runtimeexec 的讲解.docx
《关于java runtimeexec 的讲解.docx》由会员分享,可在线阅读,更多相关《关于java runtimeexec 的讲解.docx(19页珍藏版)》请在冰豆网上搜索。
关于javaruntimeexec的讲解
关于javaruntime.exec()的讲解
Runtime类,是一个与JVM运行时环境有关的类,这个类是Singleton的。
说几个自己觉得重要的地方。
1、Runtime.getRuntime()可以取得当前JVM的运行时环境,这也是在Java中唯一一个得到运行时环境的方法。
2、Runtime上其他大部分的方法都是实例方法,也就是说每次进行运行时调用时都要用到getRuntime方法。
3、Runtime中的exit方法是退出当前JVM的方法,估计也是唯一的一个吧,因为我看到System类中的exit实际上也是通过调用Runtime.exit()来退出JVM的,这里说明一下Java对Runtime返回值的一般规则(后边也提到了),0代表正常退出,非0代表异常中止,这只是Java的规则,在各个操作系统中总会发生一些小的混淆。
4、Runtime.addShutdownHook()方法可以注册一个hook在JVM执行shutdown的过程中,方法的参数只要是一个初始化过但是没有执行的Thread实例就可以。
(注意,Java中的Thread都是执行过了就不值钱的哦)
5、说到addShutdownHook这个方法就要说一下JVM运行环境是在什么情况下shutdown或者abort的。
文档上是这样写的,当最后一个非精灵进程退出或者收到了一个用户中断信号、用户登出、系统shutdown、Runtime的exit方法被调用时JVM会启动shutdown的过程,在这个过程开始后,他会并行启动所有登记的shutdownhook(注意是并行启动,这就需要线程安全和防止死锁)。
当shutdown过程启动后,只有通过调用halt方法才能中止shutdown的过程并退出JVM。
那什么时候JVM会abort退出那?
首先说明一下,abort退出时JVM就是停止运行但并不一定进行shutdown。
这只有JVM在遇到SIGKILL信号或者windows中止进程的信号、本地方法发生类似于访问非法地址一类的内部错误时会出现。
这种情况下并不能保证shutdownhook是否被执行。
现在开始看这篇文章,呵呵。
首先讲的是Runtime.exec()方法的所有重载。
这里要注意的有一点,就是publicProcessexec(String[]cmdArray,String[]envp);这个方法中cmdArray是一个执行的命令和参数的字符串数组,数组的第一个元素是要执行的命令往后依次都是命令的参数,envp我个人感觉应该和C中的execve中的环境变量是一样的,envp中使用的是name=value的方式。
--[if!
supportLists]-->1、
--[endif]-->一个很糟糕的调用程序,代码如下,这个程序用exec调用了一个外部命令之后马上使用exitValue就对其返回值进行检查,让我们看看会出现什么问题。
importjava.util.*;
importjava.io.*;
publicclassBadExecJavac{
publicstaticvoidmain(Stringargs[]){
try{
Runtimert=Runtime.getRuntime();
Processproc=rt.exec("javac");
intexitVal=proc.exitValue();
system.out.println("ProcessexitValue:
"+exitVal);
}catch(Throwablet){
t.printStackTrace();
}
}
}
ArunofBadExecJavacproduces:
E:
classescomjavaworldjpitfallsarticle2>javaBadExecJavac
java.lang.IllegalThreadStateException:
processhasnotexited
atjava.lang.Win32Process.exitValue(NativeMethod)
atBadExecJavac.main(BadExecJavac.java:
13)
这里看原文就可以了解,这里主要的问题就是错误的调用了exitValue来取得外部命令的返回值(呵呵,这个错误我也曾经犯过),因为exitValue这个方法是不阻塞的,程序在调用这个方法时外部命令并没有返回所以造成了异常的出现,这里是由另外的方法来等待外部命令执行完毕的,就是waitFor方法,这个方法会一直阻塞直到外部命令执行结束,然后返回外部命令执行的结果,作者在这里一顿批评设计者的思路有问题,呵呵,反正我是无所谓阿,能用就可以拉。
但是作者在这里有一个说明,就是exitValue也是有好多用途的。
因为当你在一个Process上调用waitFor方法时,当前线程是阻塞的,如果外部命令无法执行结束,那么你的线程就会一直阻塞下去,这种意外会影响我们程序的执行。
所以在我们不能判断外部命令什么时候执行完毕而我们的程序还需要继续执行的情况下,我们就应该循环的使用exitValue来取得外部命令的返回状态,并在外部命令返回时作出相应的处理。
2、对exitValue处改进了的程序
importjava.util.*;
importjava.io.*;
publicclassBadExecJavac2{
publicstaticvoidmain(Stringargs[]){
try{
Runtimert=Runtime.getRuntime();
Processproc=rt.exec("javac");
intexitVal=proc.waitFor();
system.out.println("ProcessexitValue:
"+exitVal);
}catch(Throwablet){
t.printStackTrace();
}
}
}
不幸的是,这个程序也无法执行完成,它没有输出但却一直悬在那里,这是为什么那?
JDK文档中对此有如此的解释:
因为本地的系统对标准输入和输出所提供的缓冲池有效,所以错误的对标准输出快速的写入和从标准输入快速的读入都有可能造成子进程的锁,甚至死锁。
文档引述完了,作者又开始批评了,他说JDK仅仅说明为什么问题会发生,却并没有说明这个问题怎么解决,这的确是个问题哈。
紧接着作者说出自己的做法,就是在执行完外部命令后我们要控制好Process的所有输入和输出(视情况而定),在这个例子里边因为调用的是Javac,而他在没有参数的情况下会将提示信息输出到标准出错,所以在下面的程序中我们要对此进行处理。
importjava.util.*;
importjava.io.*;
publicclassMediocreExecJavac{
publicstaticvoidmain(Stringargs[]){
try{
Runtimert=Runtime.getRuntime();
Processproc=rt.exec("javac");
InputStreamstderr=proc.getErrorStream();
InputStreamReaderisr=newInputStreamReader(stderr);
BufferedReaderbr=newBufferedReader(isr);
Stringline=null;
system.out.println("
while((line=br.readLine())!
=null)
system.out.println(line);
system.out.println("");
intexitVal=proc.waitFor();
system.out.println("ProcessexitValue:
"+exitVal);
}catch(Throwablet){
t.printStackTrace();
}
}
}
程序的运行结果为
E:
classescomjavaworldjpitfallsarticle2>javaMediocreExecJavac
Usage:
javac
where
-gGeneratealldebugginginfo
-g:
noneGeneratenodebugginginfo
-g:
{lines,vars,source}Generateonlysomedebugginginfo
-OOptimize;mayhinderdebuggingorenlargeclassfiles
-nowarnGeneratenowarnings
-verboseOutputmessagesaboutwhatthecompilerisdoing
-deprecationOutputsourcelocationswheredeprecatedAPIsareused
-classpath
-sourcepath
-bootclasspath
-extdirs
-d
-encoding
-target
ProcessexitValue:
2
哎,不管怎么说还是出来了结果,作者作了一下总结,就是说,为了处理好外部命令大量输出的情况,你要确保你的程序处理好外部命令所需要的输入或者输出。
下一个题目,当我们调用一个我们认为是可执行程序的时候容易发生的错误(今天晚上我刚刚犯这个错误,没事做这个练习时候发生的)
importjava.util.*;
importjava.io.*;
publicclassBadExecWinDir{
publicstaticvoidmain(Stringargs[]){
try{
Runtimert=Runtime.getRuntime();
Processproc=rt.exec("dir");
InputStreamstdin=proc.getInputStream();
InputStreamReaderisr=newInputStreamReader(stdin);
BufferedReaderbr=newBufferedReader(isr);
Stringline=null;
system.out.println("
while((line=br.readLine())!
=null)
system.out.println(line);
system.out.println("");
intexitVal=proc.waitFor();
system.out.println("ProcessexitValue:
"+exitVal);
}catch(Throwablet){
t.printStackTrace();
}
}
}
ArunofBadExecWinDirproduces:
E:
classescomjavaworldjpitfallsarticle2>javaBadExecWinDir
java.io.IOException:
CreateProcess:
direrror=2
atjava.lang.Win32Process.create(NativeMethod)
atjava.lang.Win32Process.
atjava.lang.Runtime.execInternal(NativeMethod)
atjava.lang.Runtime.exec(UnknownSource)
atjava.lang.Runtime.exec(UnknownSource)
atjava.lang.Runtime.exec(UnknownSource)
atjava.lang.Runtime.exec(UnknownSource)
atBadExecWinDir.main(BadExecWinDir.java:
12)
说实在的,这个错误还真是让我摸不着头脑,我觉得在windows中返回2应该是没有找到这个文件的缘故,可能windows2000中只有cmd命令,dir命令不是当前环境变量能够解释的吧。
我也不知道了,慢慢往下看吧。
嘿,果然和作者想的一样,就是因为dir命令是由windows中的解释器解释的,直接执行dir时无法找到dir.exe这个命令,所以会出现文件未找到这个2的错误。
如果我们要执行这样的命令,就要先根据操作系统的不同执行不同的解释程序或者cmd.exe。
作者对上边的程序进行了修改
importjava.util.*;
importjava.io.*;
classStreamGobblerextendsThread{
InputStreamis;
Stringtype;
StreamGobbler(InputStreamis,Stringtype){
this.is=is;
this.type=type;
}
publicvoidrun(){
try{
InputStreamReaderisr=newInputStreamReader(is);
BufferedReaderbr=newBufferedReader(isr);
Stringline=null;
while((line=br.readLine())!
=null)
system.out.println(type+">"+line);
}catch(IOExceptionioe){
ioe.printStackTrace();
}
}
}
publicclassGoodWindowsExec{
publicstaticvoidmain(Stringargs[]){
if(args.length<1){
system.out.println("USAGE:
javaGoodWindowsExec
system.exit
(1);
}
try{
StringosName=system.getProperty("os.name");
String[]cmd=newString[3];
if(osName.equals("WindowsNT")){
cmd[0]="cmd.exe";
cmd[1]="/C";
cmd[2]=args[0];
}
elseif(osName.equals("Windows95")){
cmd[0]="";
cmd[1]="/C";
cmd[2]=args[0];
}
Runtimert=Runtime.getRuntime();
system.out.println("Execing"+cmd[0]+""+cmd[1]
+""+cmd[2]);
Processproc=rt.exec(cmd);
//anyerrormessage?
StreamGobblererrorGobbler=new
StreamGobbler(proc.getErrorStream(),"ERROR");
//anyoutput?
StreamGobbleroutputGobbler=new
StreamGobbler(proc.getInputStream(),"OUTPUT");
//kickthemoff
errorGobbler.start();
outputGobbler.start();
//anyerror?
?
?
intexitVal=proc.waitFor();
system.out.println("ExitValue:
"+exitVal);
}catch(Throwablet){
t.printStackTrace();
}
}
}
RunningGoodWindowsExecwiththedircommandgenerates:
E:
classescomjavaworldjpitfallsarticle2>javaGoodWindowsExec"dir*.java"
Execingcmd.exe/Cdir*.java
OUTPUT>VolumeindriveEhasnolabel.
OUTPUT>VolumeSerialNumberis5C5F-0CC9
OUTPUT>
OUTPUT>DirectoryofE:
classescomjavaworldjpitfallsarticle2
OUTPUT>
OUTPUT>10/23/0009:
01p805BadExecBrowser.java
OUTPUT>10/22/0009:
35a770BadExecBrowser1.java
OUTPUT>10/24/0008:
45p488BadExecJavac.java
OUTPUT>10/24/0008:
46p519BadExecJavac2.java
OUTPUT>10/24/0009:
13p930BadExecWinDir.java
OUTPUT>10/22/0009:
21a2,282BadURLPost.java
OUTPUT>10/22/0009:
20a2,273BadURLPost1.java
...(someoutputomittedforbrevity)
OUTPUT>10/12/0009:
29p151SuperFrame.java
OUTPUT>10/24/0009:
23p1,814TestExec.java
OUTPUT>10/09/0005:
47p23,543TestStringReplace.java
OUTPUT>10/12/0008:
55p228TopLevel.java
OUTPUT>22File(s)46,661bytes
OUTPUT>19,678,420,992bytesfree
ExitValue:
0
这里作者教了一个windows中很有用的方法,呵呵,至少我是不知道的,就是cmd.exe/C+一个windows中注册了后缀的文档名,windows会自动地调用相关的程序来打开这个文档,我试了一下,的确很好用,但是好像文件路径中有空格的话就有点问题,我加上引号也无法解决。
这里作者强调了一下,不要假设你执行的程序是可执行的程序,要清楚自己的程序是单独可执行的还是被解释的,本章的结束作者会介绍一个命令行工具来帮助我们分析。
这里还有一点,就是得到process的输出的方式是getInputStream,这是因为我们要从Java程序的角度来看,外部程序的输出对于Java来说就是输入,反之亦然。
最后的一个漏洞的地方就是错误的认为exec方法会接受所有你在命令行或者Shell中输入并接受的字符串。
这些错误主要出现在命令作为参数的情况下,程序员错误的将所有命令行中可以输入的参数命令加入到exec中(这段翻译的不好,凑合看吧)。
下面的例子中就是一个程序员想重定向一个命令的输出。
importjava.util.*;
importjava.io.*;
//StreamGobbleromittedforbrevity
publicclassBadWinRedirect{
publicstaticvoidmain(Stringargs[]){
try{
Runtimert=Runtime.getRuntime();
Processproc=rt.exec("javajecho'HelloWorld'>test.txt");
//anyerrormessage?
StreamGobblererrorGobbler=new
StreamGobbler(proc.getErrorStream(),"ERROR");
//anyoutput?
StreamGobbleroutputGobbler=new
StreamGobbler(proc.getInputStream(),"OUTPUT");
//kickthemoff
errorGobbler.start();
outputGobbler.start();
//anyerror?
?
?
intexitVal=proc.waitFor();
system.out.println("ExitValue:
"+exitVal);
}catch(Throwablet){
t.printStackTrace();
}
}
}
RunningBadWinRedirectproduces:
E:
classescomjavaworldj
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 关于java runtimeexec 的讲解 关于 java 讲解