2012-02-21 87 views
2

在使用Runtime.exec()在java中創建子進程時,我知道必須填充輸入/輸出流以防止對子進程進行阻塞。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.這個死鎖是OS中的一個錯誤嗎?

+0

+1 ... *(不是你的問題的答案,因此評論)* ...請注意,很久以前(當我讀*「當Runtime.exec不會」* IIRC),我決定通過立即從Runtime.exec'ed shell腳本分支/退出並將第二個shell腳本的任何輸出重定向到臨時文件(然後從Java解析這些臨時文件)來完全避免該問題。這是殘酷的,但它的作品(我已經在Linux和OS X上做過...不知道Windows):) – TacticalCoder 2012-02-21 16:17:18

回答

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的輸出都可以放入OS緩衝區。但是,對於較大的值(> 10000應該足夠),OS緩衝區將填滿;在tr命令中,對write的調用將被阻塞,等待緩衝區被排空,並且Java代碼正在寫入的緩衝區將被填滿(因爲tr被阻止,從而阻止其從輸入讀取) ,從而阻止我們的調用out.write,導致死鎖,其中兩個進程正在等待寫入未被主動讀取的完整緩衝區。

這個死鎖不是操作系統中的一個bug,因爲進程間通信的有限緩衝區大小是故意的設計決定。另一種方法(無限緩衝區的大小)有一些缺點:

  • 可能會耗盡內核內存
  • 爲了避免上述情況,如果緩衝區自動「溢出」到磁盤,這會導致不可預知的性能,並有可能填補磁盤。

順便說一句,死鎖也可能發生由於在進程緩衝區。假設,爲了試圖解決上述的死鎖問題,我們將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

死鎖總是涉及至少2個參與者。因此,不是單獨的子進程可能會死鎖,而是父進程和子進程的組合。

有關示例:

子過程要讀取的線,並處理和打印結果。 父進程認爲它可以先從子進程讀取輸出,然後才向其提供輸入。

結果:死鎖,兩個進程都在等待來自另一個進程的輸入。

因此,不,它不是操作系統中的錯誤,而是(父)過程中的邏輯錯誤。

+0

我不確定這種問題是否由javadoc打算 - 您的問題描述的是併發環境中的一般同步問題,但與java/Process類無關。 javadoc還表示,這隻會發生在「僅提供有限緩衝區大小的某些本地平臺上」,而您描述的情況會阻止所有操作系統。 – MRalwasser 2012-02-21 15:59:51

+0

@MRalwaser - 如果是這樣的話,我確實會認爲它是操作系統中的一個bug。 「及時」的含義並不明確。只要溝通按照正確的順序完成,不論我是「及時」執行還是2天后執行,都無關緊要。 – Ingo 2012-02-21 16:07:03