process.waitfor()发生阻塞

在Java编写应用时,有时需要在程序中调用另一个线程的可执行程序或系统命令。
Process ps = Runtime.getRuntime().exec("p.exe");
ps.waitfor();

Runtime.getRuntime()返回当前应用程序的Runtime对象,该对象的exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序,
并返回与该子进程对应的Process对象实例。通过Process可以控制该子进程的执行或获取该子进程的信息。
它的所有标准io(即stdin,stdout,stderr)操作都将通过三个流(getOutputStream(),getInputStream(),getErrorStream())重定向到父进程。
父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读
写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁。
ps.waitfor()的目的是等待子进程完成再往下执行。

也就是说:如果程序不断在向标准输出流和标准错误流写数据,而JVM不读取的话,当缓冲区满之后将无法继续写入数据,最终造成阻塞在waifor()这里。

执行一个有标准输出的DOS可执行程序在windows平台上,运行被调用程序的DOS窗口执行完毕后往往并不会自动关闭,从而导致Java应用
程序阻塞在waitfor()。导致该现象的一个可能的原因是,该可执行程序的标准输出比较多,而运行窗口的标准输出缓冲区不够大。
解决的办法是, 利用Java提供的Process类提供的方法让Java虚拟机截获被调用程序的DOS运行窗口的标准输出,在waitfor()命令之前读出
窗口的标准输出缓冲区中的内容。



InputStream getErrorStream():获得子进程的错误流
InputStream getInputStream():获得子进程的输入流

可以考虑使用两个线程来同时清空process获取的两个输入流。


创建线程方法之一:实现Runnable接口

步骤:

1.定义类实现Runnable接口

2.重写run()方法,这个方法里面实现的是实际要做的操作。

3.定义一个方法,在里面实现该类的线程,同时调用线程的start()方法

例:public void start(){

         Thread thread = new Thread(this);

          thread.setDaemon(true);设置成守护线程

          thread.start();

}


public class ProcessUitl implements Runnable{
	public String getErrStr() {
		return errStr;
	}


	public void setErrStr(String errStr) {
		this.errStr = errStr;
	}


	//设置读取的字符编码
	private String character = "utf-8";
	private InputStream inputStream;
	private String errStr = null;
	
	public ProcessUitl(InputStream inputStream) {
		this.inputStream = inputStream;
	}

	@Override
	public void run() {
		BufferedReader br = null;
		try { 
			br = new BufferedReader(new InputStreamReader(inputStream, character));//以utf-8的编码格式去读取文件
			String line = null;
			StringBuilder sb = new StringBuilder();
			while((line = br.readLine()) != null){
				sb.append(line);
			}
			errStr = sb.toString();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			if(br != null){
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(inputStream != null){
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
	}
	
	public void start(){
		Thread thread = new Thread(this);
		thread.setDaemon(true);
		thread.start();
	}
	public static void main(String[] args) {
		String command = "ping www.baid.com";
		Process ps = null;
		try {
			ps = Runtime.getRuntime().exec(command);
			ProcessUitl pu = new ProcessUitl(ps.getErrorStream());
			ps.getOutputStream().close();
			ps.waitFor();
			String errorStr = pu.getErrStr();
			if(errorStr != null){
				System.out.println("Error!!!!!");
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			if(ps != null){
				ps.destroy();
			}
		}
		

	}

}




转载:以下部分

步骤:

 1.定义实现Runnable接口

 2.覆盖Runnable接口中的run方法,将线程要运行的代码存放在run方法中。

3.通过Thread类建立线程对象。

4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。

  为什么要讲Runnable接口的子类对象传递给Thread的构造方法。因为自定义的方法的所属的对象是Runnable接口的子类对象。

5.调用Thread类的start方法开启线程并调用Runnable接口子类run方法。


你可能感兴趣的:(java,线程,进程)