Java,子进程和未读输出流:什么时候死锁?

时间:2012-02-21 15:44:00

标签: java process deadlock block

使用Runtime.exec()在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.为什么会陷入僵局? 你能提供一个显示这种僵局的简短示例程序吗? 4.这个死锁是操作系统中的错误吗?

2 个答案:

答案 0 :(得分:2)

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

考虑以下代码:

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调用,导致死锁,两个进程都在等待写入不是的完整缓冲区正在积极地阅读。

此死锁不是操作系统中的错误,因为进程间通信的有限缓冲区大小是故意的设计决策。替代方案(无限缓冲区大小)有一些缺点:

  • 可以耗尽内核内存
  • 为避免上述情况,如果缓冲区自动“溢出”到磁盘,则可能导致不可预测的性能,并可能填满磁盘。

另外,由于进程内缓冲区也可能发生死锁。假设为了尝试解决上面的死锁,我们改变了我们的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 :(得分:1)

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

举个例子:

Subprocess想要读取一行,处理它并打印结果。 父进程认为它可以先读取子进程的输出,然后才为它提供输入。

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

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

相关问题