Для большого объема данных System.out.println
может быть неэффективным, так как он не очень хорошо буферизует. В этом случае вы можете использовать BufferedOutputStream
или BufferedWriter
.
Для защиты от ложного пробуждения. JVM не дает вам никаких гарантий, что единственная возможная причина, по которой поток когда-либо снова начнет работать, - это то, что вы вызвали сигнал так, как вы планировали. Иногда он просто запускается случайно и уходит (ложное пробуждение). Поэтому вам придется снова ждать, если условие, по которому вы хотите работать, на самом деле не выполняется.
Это объясняется в javadoc для метода ожидания: http://java.sun.com/javase/6/docs/api/java/lang/Object.html#wait%28long%29
И упоминается в документации для await: http://java.sun.com/javase/6/docs/api/java/util/concurrent/locks/Condition.html#await%28%29
Блокировка, связанная с этим Состояние освобождается атомарно и текущий поток становится отключенным для планирования потоков и бездействует, пока одна из четырех вещей происходит:
Другой поток вызывает метод signal () для этого условия и текущий поток оказывается выбран как нить для пробуждения; или
Какой-то другой поток вызывает метод signalAll () для этого Condition; или
Другой поток прерывает текущий поток, и прерывание поддерживается резьбовая подвеска; или
* Произошло "ложное пробуждение".
Некоторые реализации интерфейса Condition могут подавлять ложные пробуждения, но полагаться на это, следовательно, будут полагаться на детали реализации и сделать ваш код непереносимым.
Почему java.util.concurrent.ArrayBlockingQueue использует циклы while вместо if вокруг вызовов await ()?
Они используют while
вместо if
для защиты от классических условий гонки потоков в моделях производитель / потребитель и для защиты от гораздо более редких случаев ложного пробуждения.
Когда (например) несколько потребителей ожидают определенного условия (например, очередь пуста), и условие получает уведомление, есть вероятность, что другой поток может сначала заблокировать и «украсть» элемент, добавленный в очередь. Цикл while
необходим для потока, чтобы убедиться, что в очереди есть элемент, прежде чем он попытается вывести его из очереди.
Я написал несколько примеров кода и дополнительную документацию , которая демонстрирует состояние гонки.
Если посмотреть на ваш конкретный код, гонка выглядит следующим образом:
await ()
в цикле while, ожидая для того, чтобы в очереди get ()
, блокирует в очереди и должен ждать разблокировки №2 (это НЕ , ожидающий hasItems
, но ожидающий получения блокировки
) hasItems.signal ()
, чтобы уведомить кого-то о том, что там есть элемент if
, он пошел бы вперед и попытался бы выйти из очереди из пустого списка, что вызвало бы исключение ArrayIndexOutOfBoundsException
или что-то в этом роде. Причина, по которой требуется оператор while
, заключается в обработке этих условий гонки. На шаге 8 выше, с while
, поток №1 вместо этого вернется к тесту, чтобы увидеть, есть ли элементы в очереди, обнаружит, что их нет, и затем вернется в режим ожидания.
Это классическая проблема, которая сбивает с толку многих повторно въезжающих программистов. В начальных версиях Библии О'Рейли pthreads, например, был пример кода без цикла while, и их пришлось переиздать.
В некоторых потоковых системах системе легче пробудить все условия, а не конкретное условие, о котором было сообщено, так что может произойти «ложное пробуждение» . Циклы while
также защищают от этого.
Возможно, вы не поняли, но в оригинальном коде используется while вместо if, потому что может быть несколько потоков, слушающих/потребляющих очередь...
Другая проблема с вашей реализацией - это потенциально потерянный "сигнал".Если вы внимательно посмотрите на jdk impls, которые обрабатывают InterruptedException, некоторые из них сигнализируют перед возвратом. это потому, что поток может быть выбран для передачи сигналов, а затем будет прерван, и в этом случае этот сигнал будет потерян. таким образом, при прерывании jdk подразумевает повторный сигнал перед выходом из строя. поскольку этот поток мог или не мог фактически получать сигнал, это также может вызвать «ложное пробуждение» (как упоминалось ранее).