【问题标题】:Java, subprocesses and unread output streams: When does it deadlock?Java、子进程和未读输出流:什么时候死锁?
【发布时间】:2012-02-21 15:43:59
【问题描述】:

当使用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. 为什么会死锁?
3. 能否提供一个简短的示例程序来显示这种死锁?
4. 这个死锁是操作系统的bug吗?

【问题讨论】:

  • +1... (不是您的问题的答案,因此是评论)... 请注意,很久以前(当我阅读 "When Runtime .exec 不会” IIRC),我决定通过立即分叉/退出 Runtime.exec 的 shell 脚本并将第二个 shell 脚本的任何输出重定向到临时文件(然后解析这些来自 Java 的临时文件)。这很残酷,但很有效(我在 Linux 和 OS X 上都做过......不知道 Windows):)

标签: java process deadlock block


【解决方案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 的所有输出都可以放入操作系统缓冲区。但是,对于较大的值(>10000 应该就足够了),操作系统缓冲区将被填满;在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 输出一行。

【讨论】:

    【解决方案2】:

    僵局始终涉及至少 2 名参与者。所以可能死锁的不仅仅是子进程,而是父进程和子进程的结合。

    举个例子:

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

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

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

    【讨论】:

    • 我不确定这种问题是否是 javadoc 的意图 - 您描述的问题是并发环境中的一般同步问题,但与 java/Process 类无关. javadoc 还指出,这只发生在“某些仅提供有限缓冲区大小的本机平台上”,而您描述的情况将在所有操作系统上阻塞。
    • @MRalwaser - 如果是这样,那么我确实会认为它是操作系统中的错误。越是“及时”,就有点不具体了。只要沟通以正确的顺序进行,我是“立即”还是 2 天后进行都无关紧要。
    猜你喜欢
    • 2018-03-08
    • 2011-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    • 1970-01-01
    • 2016-03-26
    相关资源
    最近更新 更多