Java Process详解

Process简介

我们在实际Java开发工作中可能会遇到调用操作系统命令的场景,比如查看下文件夹,执行下sh/exe文件等等,那么我们就要用到Process了!

首先,打开API来认识下Process :

java.lang 
类 Process

java.lang.Object
  继承者 java.lang.Process
public abstract class Process
     

      extends 
      Object
     
ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获得相关信息。Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。

 

我们看到,Process是个抽象类,继承自“全民祖先”Object,有两种方式可以创建Process子类的实例,以及一系列进程交互方法:

方法摘要
abstract  void	destroy() 
          杀掉子进程。
abstract  int	exitValue() 
          返回子进程的出口值。
abstract  InputStream	getErrorStream() 
          获取子进程的错误流。
abstract  InputStream	getInputStream() 
          获取子进程的输入流。
abstract  OutputStream	getOutputStream() 
          获取子进程的输出流。
abstract  int	waitFor() 
          导致当前线程等待,如有必要,一直要等到由该 Process 对象表示的进程已经终止。

 

Process实战操练

学了那么多理论,还不如来个demo,我们来一个用“ping 百度网址”的例子吧,请看:

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,那说改就改!

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();
		}
	}
}

 

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信息):

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百度网址”的程序:

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!!!

猜你喜欢

转载自chaijuntao.iteye.com/blog/2211498