Java ProcessBuilder:输入/输出流

我想在java代码中调用外部程序,然后Google告诉我Runtime或ProcessBuilder可以帮助我完成这项工作。 我已经尝试过了,并且出现了java程序无法退出的问题,这意味着子进程和父进程都会永远等待。 他们是悬挂或死锁。

有人告诉我原因是子进程的缓存太小了。 当它试图将数据返回给父进程,但是父进程没有及时读取它时,它们都挂起了。 所以他们建议我分叉一个线程来负责读取子进程的缓存数据。 我这样做就像他们告诉我的那样,但仍有一些问题。

然后我关闭通过方法getOutputStream()获取的输出流。 最后,该计划取得了成功。 但我不知道为什么会这样? 输出流和输入流之间是否存在某种关系?

您在问题中提供的细节很少,所以我只能提供一般答案。

所有过程都有三个标准流:标准输入,标准输出和标准错误。 标准输入用于读取数据,标准输出用于写出数据,标准错误用于写出错误消息。 当您使用Runtime.getRuntime().exec()ProcessBuilder启动外部程序时,Java将为外部程序创建一个Process对象,并且此Process对象将具有访问这些流的方法。

访问这些流如下:

  • process.getOutputStream() :返回外部程序的标准输入 。 这是一个OutputStream因为它是您的Java代码将写入的内容。
  • process.getInputStream() :返回外部程序的标准输出 。 这是一个InputStream因为它是您的Java代码将从中读取的内容。
  • process.getErrorStream() :返回外部程序的标准错误。 这是一个InputStream ,就像标准输出一样,它是您的Java代码将从中读取的内容。

请注意, getInputStream()getOutputStream()可能会令人困惑。

Java代码和外部程序之间的所有流都被缓冲 。 这意味着每个流都有少量内存( 缓冲区 ),写入程序可以在其中写入尚未被读取器读取的数据。 作者不必等待读者立即读取其数据; 它可以将其输出保留在缓冲区中并继续。

有两种方法可以写入缓冲区并从中读取缓冲区:

  • 当没有足够的空间用于数据时,尝试将数据写入缓冲区,
  • 试图从空缓冲区读取。

在第一种情况下,编写器将等待,直到从缓冲区中读取数据为止。 在第二种情况下,读者将等待数据写入缓冲区。

您提到关闭getOutputStream()返回的流导致您的程序成功完成。 这将关闭外部程序的标准输入 ,告诉它没有更多内容可供阅读。 如果您的程序成功完成,则表明您的程序在挂起时正在等待更多输入。

也许有争议的是,如果您运行外部程序,如果您不需要使用它,则应该关闭其标准输入,就像您所做的那样。 这告诉外部程序将不再有输入,因此消除了它等待输入的可能性。 但是,它没有回答你的外部程序为什么等待输入的问题。

大多数情况下,当您使用Runtime.getRuntime().exec()ProcessBuilder运行外部程序时,您通常不会使用标准输入。 通常,您在命令行上将所需的任何输入传递给外部程序,然后读取其输出(如果它根本生成任何输出)。

您的外部程序是否满足您的需要,然后卡住,显然在等待输入? 您是否需要将数据发送到标准输入? 如果使用cmd.exe /k ...在Windows上启动进程,则命令解释程序将在其启动的程序退出后继续。 在这种情况下,您应该使用/c而不是/k

最后,我想强调一下有两个输出流,标准输出和标准错误。 如果您在错误的时间从错误的流中读取,可能会出现问题。 如果在缓冲区为空时尝试读取外部程序的标准输出,则Java代码将等待外部程序生成输出。 但是,如果您的外部程序正在将大量数据写入其标准错误,它可能会填充缓冲区,然后发现自己正在等待Java代码通过读取它来在缓冲区中腾出空间。 最终的结果是你的Java代码和外部程序都在等待彼此做某事,即死锁。

只需使用ProcessBuilder并确保使用true值调用其redirectErrorStream()方法即可消除此问题。 调用此方法会将外部程序的标准错误重定向到其标准输出,因此您只能读取一个流。