2012-02-21 42 views
2

在使用Runtime.exec()在java中创建子进程时,我知道必须填充输入/输出流以防止对子进程进行阻塞。Java,子进程和未读输出流:它何时死锁?

有趣的是,Process的Javadoc指出一点点:

...failure to promptly write the input stream or read the output stream of 
the subprocess may cause the subprocess to block, and even deadlock. 

我想知道,在这种情况下,子进程就可以也死锁

问题:
1.在哪些情况下会死锁?
2.它为什么会陷入僵局?
3.你能提供一个简短的例子程序来显示这个死锁吗?
4.这个死锁是OS中的一个错误吗?

+0

+1 ... *(不是你的问题的答案,因此评论)* ...请注意,很久以前(当我读*“当Runtime.exec不会”* IIRC),我决定通过立即从Runtime.exec'ed shell脚本分支/退出并将第二个shell脚本的任何输出重定向到临时文件(然后从Java解析这些临时文件)来完全避免该问题。这是残酷的,但它的作品(我已经在Linux和OS X上做过...不知道Windows):) – TacticalCoder 2012-02-21 16:17:18

回答

1

当读取任何输出之前,父级尝试将太多数据发送到其子级的输入流时,会由于缓冲区大小有限而发生死锁。

考虑以下代码:

final int LINES = 10; 
// "tr" is a Unix command that translates characters; 
// Here, for every line it reads, it will output a line with 
// every 'a' character converted to 'A'. But any command that outputs 
// something for every line it reads (like 'cat') will work here 
Process p = Runtime.getRuntime().exec("tr a A"); 
Writer out = new OutputStreamWriter(p.getOutputStream()); 
for (int i = 0; i < LINES; i++) { 
    out.write("abcdefghijklmnopqrstuvwxyz\n"); 
} 
out.close(); 
// Read all the output from the process and write it to stdout 
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); 
String line; 
while ((line = in.readLine()) != null) { 
    System.out.println(line); 
} 

lines,这将正常工作的小值;在我们开始阅读之前,所有的tr的输出都可以放入OS缓冲区。但是,对于较大的值(> 10000应该足够),OS缓冲区将填满;在tr命令中,对write的调用将被阻塞,等待缓冲区被排空,并且Java代码正在写入的缓冲区将被填满(因为tr被阻止,从而阻止其从输入读取) ,从而阻止我们的调用out.write,导致死锁,其中两个进程正在等待写入未被主动读取的完整缓冲区。

这个死锁不是操作系统中的一个bug,因为进程间通信的有限缓冲区大小是故意的设计决定。另一种方法(无限缓冲区的大小)有一些缺点:

  • 可能会耗尽内核内存
  • 为了避免上述情况,如果缓冲区自动“溢出”到磁盘,这会导致不可预知的性能,并有可能填补磁盘。

顺便说一句,死锁也可能发生由于在进程缓冲区。假设,为了试图解决上述的死锁问题,我们将Java代码改为写一行,然后交替读一行。但是,it's common for Linux processes to not flush after every line when they're not writing directly to a terminal。因此,tr可能会读取一行,并将其写入其libc输出缓冲区,而不是等待下一行被写入 - 而我们的Java代码将阻止等待tr输出一行。

1

死锁总是涉及至少2个参与者。因此,不是单独的子进程可能会死锁,而是父进程和子进程的组合。

有关示例:

子过程要读取的线,并处理和打印结果。 父进程认为它可以先从子进程读取输出,然后才向其提供输入。

结果:死锁,两个进程都在等待来自另一个进程的输入。

因此,不,它不是操作系统中的错误,而是(父)过程中的逻辑错误。

+0

我不确定这种问题是否由javadoc打算 - 您的问题描述的是并发环境中的一般同步问题,但与java/Process类无关。 javadoc还表示,这只会发生在“仅提供有限缓冲区大小的某些本地平台上”,而您描述的情况会阻止所有操作系统。 – MRalwasser 2012-02-21 15:59:51

+0

@MRalwaser - 如果是这样的话,我确实会认为它是操作系统中的一个bug。 “及时”的含义并不明确。只要沟通按照正确的顺序完成,不论我是“及时”执行还是2天后执行,都无关紧要。 – Ingo 2012-02-21 16:07:03