Process简介
我们在实际Java开发工作中可能会遇到调用操作系统命令的场景,比如查看下文件夹,执行下sh/exe文件等等,那么我们就要用到Process了!
首先,打开API来认识下Process :
Java代码
java.lang
类 Process
java.lang.Object
继承者 java.lang.Process
public abstract class Process
extends
Object
ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获得相关信息。Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。
我们看到,Process是个抽象类,继承自“全民祖先”Object,有两种方式可以创建Process子类的实例,以及一系列进程交互方法:
Java代码
- 方法摘要
- abstract void destroy()
- 杀掉子进程。
- abstract int exitValue()
- 返回子进程的出口值。
- abstract InputStream getErrorStream()
- 获取子进程的错误流。
- abstract InputStream getInputStream()
- 获取子进程的输入流。
- abstract OutputStream getOutputStream()
- 获取子进程的输出流。
- abstract int waitFor() 此方法返回的退出值的过程。按照惯例,0表示正常终止。
- 导致当前线程等待,如有必要,一直要等到由该 Process 对象表示的进程已经终止。
Process实战操练
学了那么多理论,还不如来个demo,我们来一个用“ping 百度网址”的例子吧,请看:
Java代码
- import java.io.IOException;
- public class ProcessDemo {
- public static void main(String[] args) {
- try {
- Process process = Runtime.getRuntime().exec("ping www.baidu.com");
- System.out.println("任务执行完毕!");
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
顺利运行,调用成功。但是结果是调用ping被挂到后台运行,程序直接打印了“任务执行完毕!”,而我们想要的效果是先把ping操作执行完成后,再输出“任务执行完毕!”。
好吧,Process提供了WaitFor和getInputStream两个方法,这两个方法都是阻塞java线程,等待脚本返回或结束后,再继续执行java程序,OK,那说改就改!
Java代码
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- public class ProcessDemo {
- public static void main(String[] args) {
- try {
- Process process = Runtime.getRuntime().exec("ping www.baidu.com");
- BufferedReader bufferedReader = new BufferedReader(
- new InputStreamReader(process.getInputStream(),"gbk"));
- String line;
- while ((line = bufferedReader.readLine()) != null) {
- System.out.println(line);
- }
- System.out.println("任务执行完毕!");
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
Java代码
- import java.io.IOException;
- public class ProcessDemo {
- public static void main(String[] args) {
- try {
- Process process = Runtime.getRuntime().exec("ping www.baidu.com");
- process.waitFor();
- System.out.println("任务执行完毕!");
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
这就是我们想要的啦~
问题进阶
仔细的同学可能发现了,API文档里还有这么一句话:“创建进程的方法可能无法针对某些本机平台 上的特定进程很好地工作,比如,本机窗口进程,守护进程,Microsoft Windows 上的 Win16/DOS 进程,或者 shell 脚本。创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin、 stdout 和 stderr)操作都将通过三个流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程 的输入和获得从子进程的输出。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁。”
这句话晦涩难懂,但可以简单地归纳一下,针对可执行程序的输入输出每个平台都会提供缓冲区,当没有及时把缓冲数据读出,且可执行程序在短时间内有大量数据输入缓冲区的话,缓冲区撑满,进程就会被阻塞。
好吧,那意思很清楚了,就是让我们在写Process程序的时候,尽量主动把可执行程序的输入和输出读出来,不要让它们长时间留在缓冲区。
上代码,我们先创建一个线程类,它主要负责不停地来读出Process所调用脚本的输出数据(主要的是读出error信息):
Java代码
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- public class CleanInputCache extends Thread {
- private InputStream is;
- private String type;
- public CleanInputCache(InputStream is, String type) {
- this.is = is;
- this.type = type;
- }
- public void run() {
- try {
- InputStreamReader isr = new InputStreamReader(is);
- BufferedReader br = new BufferedReader(isr);
- String line = null;
- while ((line = br.readLine()) != null)
- System.out.println(type + ">>>" + line);
- } catch (IOException ioe) {
- ioe.printStackTrace();
- }
- }
- }
然后改造一下以上的“ping百度网址”的程序:
Java代码
- import java.io.IOException;
- public class ProcessDemo {
- public static void main(String[] args) {
- try {
- Process process = Runtime.getRuntime().exec("ping www.baidu.com");
- new CleanInputCache(process.getInputStream(),"INFO").start();
- new CleanInputCache(process.getErrorStream(),"ERROR").start();
- process.waitFor();
- System.out.println("任务执行完毕!");
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
大功告成,这样程序再也不用担心阻塞了,放心奔跑吧,process!!!