java.lang.Process 阻塞问题

Process 类型对象的 waitFor() 方法的时候当前线程会被阻塞掉,因为InputStream in = Process.getInputStream();或 InputStream err = Process.getErrorStream(); 这2个流中的一个会被阻塞住,所以解决的办法可以是用多线程来解决,可以参看http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps_p.html 。 

但还有另一种方法可以尝试,

Java代码
  1. String[] cmds = {...};//command and arg  
  2. ProcessBuilder builder = new ProcessBuilder(cmds);  
  3. //both read inputstream and errstream  
  4. builder.redirectErrorStream(true);  
  5. Process process = builder.start();  
  6. BufferedReader br = new BufferedReader(new InputStreamReader(process  
  7.     .getInputStream()));  
  8. String readLine = br.readLine();  
  9. while (readLine != null) {  
  10.    System.out.println(readLine);  
  11.    readLine = br.readLine();  
  12. }  
  13. int resultValue = process.waitFor(); 

builder.redirectErrorStream(true); 和BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); 这样使用就可以在一个线程中执行,而保证不被阻塞住。

-----------------------------------------------------------------------------------------------
jdk中的描述
public ProcessBuilder redirectErrorStream(boolean redirectErrorStream)
设置此进程生成器的 redirectErrorStream 属性。

如果此属性为 true,则任何由通过此对象的 start() 方法启动的后续子进程生成的错误输出都将与标准输出合并,因此两者均可使用 Process.getInputStream() 方法读取。这使得关联错误消息和相应的输出变得更容易。初始值为 false

==============================================================

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
{
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;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值