2012-07-20 265 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