2012-07-20 154 views
1

我想调用java代码中的外部程序,然后Google告诉我Runtime或ProcessBuilder可以帮助我完成这项工作。我已经尝试过了,出现了一个java程序无法退出的问题,这意味着子进程和父进程都会永远等待。他们挂着或者陷入僵局。Java ProcessBuilder:输入/输出流

有人告诉我,原因是子进程的缓存太小。当它试图将数据返回给父进程时,但父进程不会及时读取它们,则两者都会挂起。所以他们建议我派一个线程来负责读取子进程的缓存数据。我按照他们的意见去做,但仍然存在一些问题。

然后关闭通过方法getOutputStream()获取的输出流。最后,方案成功。但我不知道为什么会发生?输出蒸汽与输入流之间是否存在某种关系?

+0

你试图运行什么外部程序?你可以在命令提示符/ Terminal/shell中运行这个程序吗? – 2012-07-20 14:16:45

回答

19

你在问题中提供了很少的细节,所以我只能提供一个普遍的答案。

所有进程都有三个标准流:标准输入,标准输出和标准错误。标准输入用于读取数据,标准输出用于写出数据,标准错误用于写出错误消息。当您使用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()方法即可简化该问题。调用此方法会将外部程序的标准错误重定向到其标准输出,因此只有一个流可以读取。

+0

很好的解释!非常感谢 – neo1691 2014-06-06 09:06:16