Возможно возникновение взаимоблокировки из-за ограниченного размера буфера, когда родительский процесс пытается отправить слишком много данных во входной поток своего дочернего элемента перед чтением любого из выходных данных.
Рассмотрим этот код:
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-код, написав одну строку, а затем поочередно прочитав одну строку.Однако обычно для процессов Linux не сбрасывается после каждой строки, когда они не записывают напрямую в терминал .Таким образом, tr
может прочитать строку и записать ее в свой выходной буфер libc, а затем заблокировать, ожидая записи следующей строки - и наш Java-код заблокирует ожидание tr
для вывода строки.