Process阻塞问题

Java中

Runtime.getInstance().exec (String cmd)

或者

new ProcessBuilder(String cmd).start()

都可以产生子进程对象Process。通过调用Process对象的waitFor()方法可以使主进程进入等待状态,直至子进程执行完毕,再进行下一步工作。如果对子进程处理不当,有可能造成主进程阻塞,整个程序死掉。

java Api中关于Process说的是:

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

创建进程的方法可能无法针对某些本机平台上的特定进程很好地工作,比如,本机窗口进程,守护进程,Microsoft Windows 上的 Win16/DOS 进程,或者 shell 脚本。创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin,stdout,stderr)操作都将通过三个流 (getOutputStream()getInputStream()getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁。

在对getOutputStream()getInputStream()getErrorStream()的描述中,有个注意事项:对其输出流和错误流进行缓冲是一个好主意!嗯,好抽象啊!

问题正在于此,Process.getInputStream()和Process.getErrorStream()分别返回Process的标准输出流和错误流,两个流如果处理不当,其缓冲区不能被及时清除而被塞满,则进程被阻塞,即使调用Process.destory()也未必能销毁被阻塞的子进程。


如果尝试同步获取Process的输出流和错误流进行处理,未必有效,顺序执行过程中,输出流和错误流常常不能得到及时处理。解决方案有两个。


方案一:并发获取Process的输出流和错误流。

通过启动两个线程来并发地读取和处理输出流和错误流,懒得打开IDE了,就大概敲一下代码吧,可能有错误,如下:

调用者:

class ProcessExecutor
{
	private Process p;
	private List<String> outputList;
	private List<String> errorOutputList;
	public ProcessExecutor(Process p) throws IOException
	{
		if(null == p)
		{
			throw new IOException("the provided Process is null");
		}
		this. p = p;
	}
	public List<String> getOutputList()
	{
		return this. outputList;
	}
	public List<String> getErrorOutputList()
	{
		return this.errorOutputList;
	}
	public int execute()
	{
		int rs = 0;
		Thread outputThread = new ProcessOutputThread(this.p.getInputStream());
		Thread errorOutputThread = new ProcessOutputThread(this.p.getErrorStream());
		outputThread.start();
		errorOutputThread.start();
		rs = p.waitFor();
		outputThread.join();
		errorOutputThread.join();
		this.outputList = outputThread.getOutputList();
		this.errorOutputList = errorOutputThread.getOutputList();
		return rs;
	}
}


流处理线程

class ProcessOutputThread extends Thread
{
	private InputStream is;
	private List<String> outputList;
	public ProcessOutputThread(InputStream is) throws IOException
	{
		if(null == is)
		{
			throw new IOException("the provided InputStream is null");
		}
		this. is = is;
		this.outputList = new ArrayList<String>();
	}
	public List<String> getOutputList()
	{
		return this. outputList;
	}
	@Override
	public void run()
	{
		InputStreamReader ir = null;
		BufferedReader br = null;
		try
		{
			ir = new InputStreamReader(this.is);
			br = new BufferedReader(ir);
			String output = null;
			while(null != (output = br.readLine()))
			{
				print(output);
				this.outputList.add(output);
			}
		}
		catch(IOException e)
		{
			e.print();
		}
		finally
		(
			try
			{
				if(null != br)
				{
					br.close();
				}
				if(null != ir)
				{
					ir.close();
				}
				if(null != this.is)
				{
					this.is.close();
				}
			}
			catch(IOException e)
			{
				e.print();
			}
		)
	}
}


方案二:用ProcessBuilder的redirectErrorStream()方法合并输出流和错误流。

public int execute()
{
	int rs = 0;
	String[] cmds = {...};//command and arg  
	ProcessBuilder builder = new ProcessBuilder(cmds);  
	builder.redirectErrorStream(true);  
	Process process = builder.start();  
	BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));  
	String output = null;  
	while (null != (readLine = br.readLine()))
	{  
   		print(output);   
	}  
	rs = process.waitFor();
	return rs;
} 


  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Java的Process类是用于执行操作系统进程的类。它允许Java程序启动外部程序,并与其进行通信。Process类提供了许多方法来控制和监视外部进程的执行,包括启动进程、等待进程结束、获取进程的输入流和输出流等。 要使用Process类,需要使用Runtime类的exec()方法来启动外部进程,并返回一个Process对象。通过这个Process对象,可以获取进程的输入流、输出流和错误流,并通过这些流与进程进行通信。 下面是一个简单的示例代码,演示了如何使用Process类执行外部命令并获取其输出: ```java import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; public class ProcessExample { public static void main(String[] args) { try { Process process = Runtime.getRuntime().exec("ls -l"); // 获取进程的输入流 InputStream inputStream = process.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = reader.readLine()) != null) { System.out.println(line); } // 等待进程结束 int exitCode = process.waitFor(); System.out.println("Exit code: " + exitCode); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } } ``` 以上代码会启动一个外部进程执行"ls -l"命令,并将命令的输出打印到控制台。最后,程序会等待进程执行结束并获取退出码。 请注意,使用Process类执行外部命令时,需要注意处理进程的输入流、输出流和错误流,以避免进程阻塞或产生错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值