java如何添加进程_如何创建⼀个进程,如何进程调⽤进程java⼀般⽤线程就够了,多进程优势在于每个进程互不⼲扰,劣势在于太耗费内存,任何事情都不是绝对的,在编写Java程序时,有时候需要在Java程序中执⾏另外⼀个程序。
1、启动程序
Java提供了两种⽅法⽤来启动其它程序:
(1)使⽤Runtime的exec()⽅法
(2)使⽤ProcessBuilder的start()⽅法
不管在哪种操作系统下,程序具有基本类似的⼀些属性。⼀个程序启动后就程序操作系统的⼀个进程,进程在执⾏的时候有⾃⼰的环境变量、有⾃⼰的⼯作⽬录。Runtime和ProcessBuilder提供了不同的⽅式来启动程序,设置启动参数、环境变量和⼯作⽬录。
能够在Java中执⾏的外部程序,必须是⼀个实际存在的可执⾏⽂件,对于shell下的内嵌命令是不能直接执⾏的。
采⽤Runtime的exec执⾏程序时,⾸先要使⽤Runtime的静态⽅法得到⼀个Runtime,然后调⽤Runtime
的exec⽅法。可以将要执⾏的外部程序和启动参数、环境变量、⼯作⽬录作为参数传递给exec⽅法,该⽅法执⾏后返回⼀个Process代表所执⾏的程序。
Runtime有六个exec⽅法,其中两个的定义为:
public Process exec(String[] cmdarray, String[] envp, File dir)
public Process exec(String command, String[] envp, File dir)
cmdarray和command为要执⾏的命令,可以将命令和参数作为⼀个字符串command传递给exec()⽅法,也可以将命令和参数⼀个⼀个的⽅在数组cmdarray⾥传递给exec()⽅法。
envp为环境变量,以name=value的形式放在数组中。dir为⼯作⽬录。
可以不要dir参数,或者不要envp和dir参数,这样就多出了其它4个exec()⽅法。如果没有dir参数或者为null,那么新启动的进程就继承当前java进程的⼯作⽬录。如果没有envp参数或者为null,那么新启动的进程就继承当前java进程的环境变量。
也可以使⽤ProcessBuilder类启动⼀个新的程序,该类是后来添加到JDK中的,⽽且被推荐使⽤。通过构造函数设置要执⾏的命令以及参数,或者也可以通过command()⽅法获取命令信息后在进⾏设置。通过directory(File directory) ⽅法设置⼯作⽬录,通过
environment()获取环境变量信息来修改环境变量。
在使⽤ProcessBuilder构造函数创建⼀个新实例,设置环境变量、⼯作⽬录后,可以通过start()⽅法来启动新程序,与Runtime的exec()⽅法⼀样,该⽅法返回⼀个Process对象代表启动的程序。
ProcessBuilder与()⽅法的不同在于ProcessBuilder提供了redirectErrorStream(boolean redirectErrorStream) ⽅法,该⽅法⽤来将进程的错误输出重定向到标准输出⾥。即可以将错误输出都将与标准输出合并。
2、Process
不管通过那种⽅法启动进程后,都会返回⼀个Process类的实例代表启动的进程,该实例可⽤来控制进程并获得相关信息。Process 类提供了执⾏从进程输⼊、执⾏输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的⽅法:
(1) void destroy()
杀掉⼦进程。
⼀般情况下,该⽅法并不能杀掉已经启动的进程,不⽤为好。
(2) int exitValue()
返回⼦进程的出⼝值。
只有启动的进程执⾏完成、或者由于异常退出后,exitValue()⽅法才会有正常的返回值,否则抛出异常。
(3)InputStream getErrorStream()
获取⼦进程的错误流。
如果错误输出被重定向,则不能从该流中读取错误输出。
(4)InputStream getInputStream()
获取⼦进程的输⼊流。
可以从该流中读取进程的标准输出。
(5)OutputStream getOutputStream()
获取⼦进程的输出流。
写⼊到该流中的数据作为进程的标准输⼊。
(6) int waitFor()
导致当前线程等待,如有必要,⼀直要等到由该 Process 对象表⽰的进程已经终⽌。
通过该类提供的⽅法,可以实现与启动的进程之间通信,达到交互的⽬的。
3、从标准输出和错误输出流读取信息
从启动其他程序的Java进程看,已启动的其他程序输出就是⼀个普通的输⼊流,可以通过getInputStream()和getErrorStream来获取。对于⼀般输出⽂本的进程来说,可以将InputStream封装成BufferedReader,然后就可以⼀⾏⼀⾏的对进程的标准输出进⾏处理。
4、举例
(1)()
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
public class Test1 {
public static void main(String[] args) {
try {
Process p = null;
String line = null;
BufferedReader stdout = null;
//list the files and directorys under C:\
p = Runtime().exec(" /C dir", null, new File("C:\\"));
stdout = new BufferedReader(new InputStreamReader(p
.getInputStream()));
while ((line = adLine()) != null) {
System.out.println(line);
}
stdout.close();
//echo the value of NAME
p = Runtime().exec(" /C echo %NAME%", new String[] {"NAME=TEST"}); stdout = new BufferedReader(new InputStreamReader(p
.getInputStream()));
while ((line = adLine()) != null) {
System.out.println(line);
}
stdout.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
(2)ProcessBuilder
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
public class Test2 {
public static void main(String[] args) {
try {
List list = new ArrayList();
ProcessBuilder pb = null;
Process p = null;
String line = null;
BufferedReader stdout = null;
//list the files and directorys under C:\
list.add("CMD.EXE");
list.add("/C");
list.add("dir");
pb = new ProcessBuilder(list);
pb.directory(new File("C:\\"));
p = pb.start();
stdout = new BufferedReader(new InputStreamReader(p
.getInputStream()));
while ((line = adLine()) != null) {
System.out.println(line);
}
stdout.close();
//echo the value of NAME
pb = new ProcessBuilder();
pbmand(new String[] {"", "/C", "echo %NAME%"});
p = pb.start();
一个线程可以包含多个进程stdout = new BufferedReader(new InputStreamReader(p
.getInputStream()));
while ((line = adLine()) != null) {
System.out.println(line);
}
stdout.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
5、获取进程的返回值
通常,⼀个程序/进程在执⾏结束后会向操作系统返回⼀个整数值,0⼀般代表执⾏成功,⾮0表⽰执⾏出现问题。有两种⽅式可以⽤来获取进程的返回值。⼀是利⽤waitFor(),该⽅法是阻塞的,执导进程执⾏完成后再返回。该⽅法返回⼀个代表进程返回值的整数值。另⼀个⽅法是调⽤exitValue()⽅法,该⽅法是⾮阻塞的,调⽤⽴即返回。但是如果进程没有执⾏完成,则抛出异常。
6、阻塞的问题
由Process代表的进程在某些平台上有时候并不能很好的⼯作,特别是在对代表进程的标准输⼊流、输出流和错误输出进⾏操作时,如果使⽤不慎,有可能导致进程阻塞,甚⾄死锁。
如果将以上事例中的从标准输出重读取信息的语句修改为从错误输出流中读取:
stdout = new BufferedReader(new InputStreamReader(p
.getErrorStream()));
那么程序将发⽣阻塞,不能执⾏完成,⽽是hang在那⾥。
当进程启动后,就会打开标准输出流和错误输出流准备输出,当进程结束时,就会关闭他们。在以上例⼦中,错误输出流没有数据要输出,标准输出流中有数据输出。由于标准输出流中的数据没有被读取,进程就不会结束,错误输出流也就不会被关闭,因此在调⽤readLine()⽅法时,整个程序就会被阻塞。为了解决这个问题,可以根据输出的实际先后,先读取标准输出流,然后读取错误输出流。
但是,很多时候不能很明确的知道输出的先后,特别是要操作标准输⼊的时候,情况就会更为复杂。这时候可以采⽤线程来对标准输出、错误输出和标准输⼊进⾏分别处理,根据他们之间在业务逻辑上的关系决定读取那个流或者写⼊数据。
针对标准输出流和错误输出流所造成的问题,可以使⽤ProcessBuilder的redirectErrorStream()⽅法将他们合⼆为⼀,这时候只要读取标准输出的数据就可以了。
当在程序中使⽤Process的waitFor()⽅法时,特别是在读取之前调⽤waitFor()⽅法时,也有可能造成阻塞。可以⽤线程的⽅法来解决这个问题,也可以在读取数据后,调⽤waitFor()⽅法等待程序结束。
总之,解决阻塞的⽅法应该有两种:
(1)使⽤ProcessBuilder类,利⽤redirectErrorStream⽅法将标准输出流和错误输出流合⼆为⼀,在⽤start()⽅法启动进程后,先从标准输出中读取数据,然后调⽤waitFor()⽅法等待进程结束。
如:
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
public class Test3 {
public static void main(String[] args) {
try {
List list = new ArrayList();
ProcessBuilder pb = null;
Process p = null;
String line = null;
BufferedReader stdout = null;
//list the files and directorys under C:\
list.add("CMD.EXE");
list.add("/C");
list.add("dir1");
pb = new ProcessBuilder(list);
pb.directory(new File("C:\\"));
/
/merge the error output with the standard output
p = pb.start();
//read the standard output
stdout = new BufferedReader(new InputStreamReader(p
.getInputStream()));
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论