Java中ProcessBuilder.start()和Runtime.exec()方法都被用来创建一个操作系统进程(执行命令行操作),并返回 Process 子类的一个实例,该实例可用来控制进程状态并获得相关信息。
Process类
Process类提供了从进程输入、输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。
Java中创建的Process子进程没有自己的终端或控制台;它的所有标准IO重定向到父进程,父进程使用这些流来提供到子进程的输入和获得从子进程的输出:
- 子进程的stdin通过Process的getOutputStream()获取
- 子进程的stdout通过Process的getInputStream()获取
- 子进程的stderr通过Process的getErrorStream()获取
因为平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流出现失败(或未读取),则可能导致子进程阻塞,甚至产生死锁。
当没有Process对象的更多引用时(在Java中离开作用域时),不是杀掉子进程,而是继续异步执行子进程。若要杀掉子进程,需通过destroy()函数来实现。
Runtime.exec方式
Runtime.getRuntime()提供了多个重载方法以启动子进程:可接受一个单独的字符串(通过空格来分隔可执行命令程序和参数);也可以接受字符串数组参数。
命令行启动
通过windows的cmd /c
+要运行的命令可以方便的启动所需的程序。通过此命令也可以一次执行多条命令:
cmd /c cd c:/mypath && start mypro.exe
通过命令行执行启动程序时,程序启动完成后命令行会退出,所以Java中对应的Process也会完成退出。
public void runCommand(String strCmd){
// cmd /c cd c:\tmp && start test.ext
String strRun = "cmd /c";
strRun += strCmd;
try{
Process proc = Runtime.getRuntime().exec(strRun);
if(!proc.waitFor(1, TimeUnit.SECONDS)){
System.out.println("Wait timeout!!!");
}
}
catch(Exception ex){
ex.printStackTrace();
}
}
直接启动
明确知晓要运行的程序与对应参数的情况下,也可直接通过exec启动进程。此时,若进程通过stdout有输出,则需要通过getInputStream来读取(否则,若缓冲区满时,则子进程会被阻塞掉);若没有,则不需要处理。
public void runApp(String strExe, String strParam, String strPath) {
Thread thr = new Thread(() -> {
String strRun = Paths.get(strPath, strExe).toString();
strRun += " " + strParam;
File fPath = new File(strPath);
try {
Process proc = Runtime.getRuntime().exec(strRun, null, fPath);
InputStream inStream = proc.getInputStream();
try (InputStreamReader reader = new InputStreamReader(inStream);
BufferedReader br = new BufferedReader(reader);) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
});
thr.start();
}
ProcessBuilder方式
ProcessBuilder实例管理一个进程属性集,ProcessBuilder的start()方法利用这些属性创建一个新的Process实例。start()方法可以从同一实例重复调用,以利用相同或者相关的属性创建新的子进程。
public void getIPv4Address() {
ProcessBuilder pbGet = new ProcessBuilder("ipconfig", "/all");
try {
Process process = pbGet.start();
try (InputStreamReader inReader = new InputStreamReader(process.getInputStream(), "GBK");
BufferedReader br = new BufferedReader(inReader);) {
String line;
while ((line = br.readLine()) != null) {
if (line.indexOf("IPv4") != -1) {
System.out.println(line);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}