Java InputStream блокирует чтение

Согласно Java API, InputStream.read() описывается как:

Если байт недоступен из-за достижения конца потока, возвращается значение -1. Этот метод блокируется до тех пор, пока не будут доступны входные данные, не будет обнаружен конец потока или сгенерировано исключение.

у меня есть while(true) цикл делает чтение, и я всегда получаю -1, когда по потоку ничего не отправляется. Это ожидается.

Мой вопрос, когда будет читать () когда-либо блокировать? Поскольку, если он не получает никаких данных, он возвращает -1. Я ожидаю, что блокирующее чтение будет ждать, пока данные не будут получены. Если вы достигли конца входного потока, не следует ли read() просто ждать данных вместо возврата -1?

Или read() блокируется, только если другой поток обращается к потоку, а ваш read() не может получить доступ к потоку?


Что приводит меня к моему следующему вопросу. Раньше у меня был прослушиватель событий (предоставленный моей библиотекой), который уведомлял меня, когда данные были доступны. Когда мне сообщили, я позвоню while((aByte = read()) > -1) сохранить байт. Я был озадачен, когда получил два события в непосредственной близости, и не все мои данные отображались. Казалось, что будет отображаться только хвостовая часть данных второго события, а остальные отсутствуют.

В конце концов я изменил свой код, так что когда я получил событие, я позвонил if(inputStream.available() > 0) while((aByte = read()) > -1) сохранить байт. Теперь все работало нормально и все мои данные отображались.

Может кто-нибудь объяснить это поведение? InputStream.available() Говорят, что возвращает количество байтов, которые вы можете прочитать перед блокировкой следующего вызывающего (потока?). Даже если я не использую.available(), я ожидаю, что чтение первого события просто заблокирует чтение второго события, но не удалит или не использует слишком много потоковых данных. Почему при этом не отображаются все мои данные?

8 ответов

Базовый источник данных для некоторых реализаций InputStream может сигнализировать о том, что конец потока достигнут, и данные больше не будут отправляться. Пока этот сигнал не получен, операции чтения в таком потоке могут блокироваться.

Например, InputStream из Socket Сокет будет блокировать, а не возвращать EOF, до тех пор, пока не будет получен пакет TCP с установленным флагом FIN. Когда EOF получен из такого потока, вы можете быть уверены, что все данные, отправленные по этому сокету, были надежно получены, и вы не сможете больше читать данные. (Если блокирующее чтение приводит к исключению, с другой стороны, некоторые данные могут быть потеряны.)

Другие потоки, например потоки из необработанного файла или последовательного порта, могут не иметь аналогичного формата или протокола, чтобы указывать, что больше нет данных. Такие потоки могут немедленно возвращать EOF (-1), а не блокировать, когда в данный момент нет доступных данных. Однако в отсутствие такого формата или протокола вы не можете быть уверены, когда другая сторона закончит отправку данных.


Что касается вашего второго вопроса, похоже, что вы, возможно, имели состояние гонки. Не видя рассматриваемого кода, я предполагаю, что проблема на самом деле заключается в вашем методе "отображения". Возможно, попытка отображения во втором уведомлении каким-то образом затрудняет работу, выполненную во время первого уведомления.

Возвращает -1, если это конец потока. Если поток все еще открыт (т.е. соединение через сокет), но данные не достигли стороны чтения (сервер работает медленно, сети работают медленно...), блоки read ().

Вам не нужно звонить доступны (). Мне трудно понять ваш дизайн уведомлений, но вам не нужны никакие вызовы, кроме самого read (). Метод available () доступен только для удобства.

Хорошо, это немного путаница, поэтому сначала давайте разберемся с этим: блокировка InputStream.read() не имеет ничего общего с многопоточностью. Если у вас есть несколько потоков, читающих из одного и того же входного потока, и вы запускаете два события очень близко друг к другу - где каждый поток пытается использовать событие, то вы получите повреждение: первый поток для чтения получит несколько байтов (возможно, все байты), и когда второй поток будет запланирован, он будет читать остальные байты. Если вы планируете использовать один поток ввода-вывода в более чем одном потоке, всегда синхронизируйте его по некоторым внешним ограничениям.

Во-вторых, если вы можете читать из вашего InputStream до тех пор, пока не получите -1, а затем подождать и можете читать снова позже, то используемая вами реализация InputStream не работает! В контракте для InputStream четко указывается, что InputStream.read() должен возвращать -1 только тогда, когда больше нет данных для чтения, потому что конец всего потока достигнут, и больше данных никогда не будет доступно - как при чтении из файл, и вы дошли до конца.

Поведение для "больше нет доступных данных, пожалуйста, подождите, и вы получите больше" для read (), чтобы заблокировать и не возвращать, пока не будут доступны некоторые данные (или будет сгенерировано исключение).

По умолчанию поведение предоставленного RXTX InputStream не соответствует.

Вы должны установить порог приема на 1 и отключить тайм-аут приема:

serialPort.enableReceiveThreshold(1);
serialPort.disableReceiveTimeout();

Источник: RXTX последовательное соединение - проблема с блокировкой чтения ()

Да! Не сдавайся в своем эфире пока Jbu. Мы говорим о последовательной связи здесь. Для последовательных устройств абсолютно ожидаемо, что -1 может / будет возвращено при чтении, но все же ожидается получение данных в более позднее время. Проблема в том, что большинство людей привыкли иметь дело с TCP/IP, который всегда должен возвращать 0, если только TCP/IP не отключен... тогда да, -1 имеет смысл. Однако в Serial отсутствует поток данных в течение длительных периодов времени, а также "HTTP Keep Alive", или пульс TCP/IP, или (в большинстве случаев) нет аппаратного управления потоком. Но связь физическая, и все же связана с "медью" и все же прекрасно жива.

Теперь, если то, что они говорят, правильно, то есть: Serial должен быть закрыт на -1, то почему мы должны следить за такими вещами, как OnCTS, pmCarroerDetect, onDSR, onRingIndicator и т. Д... Черт, если 0 означает, что это там, а -1 означает, что нет, тогда вверните все эти функции обнаружения!:-)

Проблема, с которой вы можете столкнуться, может быть в другом месте.

Теперь перейдем к конкретике:

Q: "Казалось, что будет отображаться только хвостовая часть данных второго события, а остальные отсутствуют".

A: Я собираюсь догадаться, что вы были в цикле, повторно используя один и тот же буфер byte[]. 1-е сообщение поступает, еще не отображается на экране /log/std out (поскольку вы находитесь в цикле), затем вы читаете 2-е сообщение, заменяя данные 1-го сообщения в буфере. Опять же, потому что я собираюсь догадаться, что вы не сохраняете то, сколько вы читаете, а затем убедитесь, что вы сместили буфер хранилища на предыдущую сумму чтения.

В: "В конце концов я изменил свой код так, чтобы при получении события я вызывал if(inputStream.available() > 0), а ((aByte = read()) > -1) сохранял байт".

A: Браво... там все хорошо. Теперь, ваш буфер данных находится внутри оператора IF, ваше второе сообщение не будет загромождать ваше первое... ну, на самом деле, это было, вероятно, только одно большое (er) сообщение на первом месте. Но теперь вы будете читать все сразу, сохраняя данные в целости и сохранности.

C: "... состояние гонки..."

A: Аааа, хорошо, поймай всех козлов! Состояние гонки...:-) Да, это могло быть условие гонки, на самом деле, возможно, это было хорошо. Но это также может быть способ, которым RXTX очищает флаг. Снятие "флага доступности данных" может произойти не так быстро, как ожидается. Например, кто-нибудь знает разницу между read VS readLine в отношении очистки буфера, в котором ранее были сохранены данные, и повторной установки флага события? Я тоже.:-) И пока не могу найти ответ... но... позвольте мне еще немного поговорить. Событийное программирование все еще имеет некоторые недостатки. Позвольте мне привести пример из реальной жизни, с которым я недавно столкнулся.

  • Я получил некоторые данные TCP/IP, скажем, 20 байтов.
  • Поэтому я получаю OnEvent для полученных данных.
  • Я начинаю свое "чтение" даже с 20 байтов.
  • Прежде чем я закончу читать мои 20 байтов... я получаю еще 10 байтов.
  • TCP/IP, однако, выглядит, чтобы уведомить меня, о, видит, что флаг все еще установлен, и не будет уведомлять меня снова.
  • Тем не менее, я заканчиваю читать свои 20 байтов (доступно () сказал, что было 20)...
  • ... и последние 10 байтов остаются в TCP/IP Q... потому что я не был уведомлен о них.

Видите ли, уведомление было пропущено, потому что флаг все еще был установлен... хотя я начал читать байты. Если бы я закончил байты, флаг был бы очищен, и я получил бы уведомление для следующих 10 байтов.

Точная противоположность того, что происходит сейчас с тобой.

Так что, да, перейдите с IF available() ... прочитайте возвращенную длину данных. Затем, если вы параноик, установите таймер и снова вызовите available (), если там все еще есть данные, тогда выполните чтение без новых данных. Если available () возвращает 0 (или -1), тогда расслабьтесь... расслабьтесь... и дождитесь следующего уведомления OnEvent.

InputStream это просто абстрактный класс, к сожалению, реализация решает, что произойдет.

Что происходит, если ничего не найдено:

  • Розетки (т.е. SocketInputStream) будет блокироваться до получения данных (по умолчанию). Но можно установить время ожидания (см.: setSoTimeout), то read будет блокироваться на х мс. Если все еще ничего не получено тогда SocketTimeoutException будет брошен.

    Но с или без тайм-аута, чтение из SocketInputStream иногда может привести к -1 , (Например, когда несколько клиентов одновременно подключаются к одному host:port то, хотя устройства кажутся подключенными, результат read может немедленно привести к -1 (никогда не возвращая данные).)

  • Сериалио общение всегда вернется -1; Вы также можете установить тайм-аут (используйте setTimeoutRx), read будет сначала заблокирован на х мс, но результат все равно будет -1 если ничего не найдено (Примечание: но доступно несколько последовательных классов ввода-вывода, поведение может зависеть от поставщика.)

  • Файлы (читатели или потоки) приведут к EOFException,

Работа для общего решения:

  • Если вы оберните любой из вышеперечисленных потоков в DataInputStream тогда вы можете использовать такие методы, как readByte, readChar, так далее. Все -1 значения преобразуются в EOFException , (PS: если вы выполняете много небольших чтений, то это хорошая идея, чтобы обернуть его в BufferedInputStream первый)
  • И то и другое SocketTimeoutException а также EOFException простираться IOException и есть несколько других возможных IOException "S. Удобно просто проверить IOException для выявления проблем со связью.

Еще одна деликатная тема - смывание. flush в терминах сокетов означает "отправить его сейчас", но в терминах Serialio это означает "сбросить буфер".

Я использую Eclipse Jetty 9.2.2. Я удивлен, увидев, что 10-12% моего запроса занимают около 500 с лишним секунд каждый, в то время как другие обрабатываются в течение 2-3 мсек каждый. часть кода, принимающего

timeBufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));

String line = null;
String postData = "";
while ((line = br.readLine()) != null){ // this is the line taking all time
    postData+=line;
}

br.close();

Я думаю, что вы можете получить весь поток данных, если вы используете thread.sleep()

Другие вопросы по тегам