A операция блокировки - это операция, которая блокирует поток до завершения операции. Блокировка поток - это процесс указания планировщику потока (обычно это операционная система, хотя есть библиотеки потоков пользовательского уровня) не запускать поток, пока этот поток не будет разбужен.Существует много видов операций блокировки, и одним из примеров является файловый ввод-вывод.Как и в случае любой другой операции блокировки, метод не возвращается до тех пор, пока не завершится соответствующая операция (в данном случае файловый ввод / вывод).
A wait - это особый вид блокировкиоперация, используемая для синхронизации потоков.В частности, он говорит: «Пожалуйста, блокируйте поток, который вызывал wait
, пока какой-то другой поток не разбудит его».В Java ожидание - это метод .Соответствующий метод активизации: notify .
A lock - это абстракция более высокого уровня, которая говорит "разрешить только ограниченное количество потоков в эту область кода".«.Чаще всего это ограниченное число равно 1, и в этом случае мьютекс (который я подробно объясняю в этом ответе SO ) является предпочтительным блокирующим примитивом в языке более низкого уровня, таком как C. В JavaНаиболее распространенный блокирующий примитив называется monitor .Существует понятие владения монитором объекта (у каждого объекта есть монитор), ожидания на мониторе и пробуждения потока, ожидающего на мониторе.Как нам это сделать?Вы уже догадались - мы используем метод wait
для ожидания на мониторе и notify
для пробуждения одного из потоков, ожидающих на мониторе.
Теперь ответ, который, вероятно, будет звучать немногокак греческий, учитывая, что вы только начинаете с параллелизма: для реализации шаблона производитель-потребитель наиболее распространенной стратегией является использование двух семафоров (плюс мьютекс для синхронизации доступа к буферу).Семафор обычно реализуется с помощью мьютекса, но это конструкция более высокого порядка, поскольку она позволяет подсчитывать некоторый ресурс.Таким образом, вы оставляете один семафор для подсчета количества элементов в буфере и один для подсчета количества пустых мест в буфере.Производитель ожидает семафор пустого пространства и добавляет элементы в буфер всякий раз, когда пространство становится доступным, а потребитель ожидает семафор элементов и потребляет элемент всякий раз, когда элемент становится доступным.
Теперь я определил, что этивещи есть, но я на самом деле не говорил о том, как использовать их.Это, однако, стоит нескольких лекций в курсе колледжа, и, конечно, слишком много для ответа StackOverflow.Я бы рекомендовал уроки параллелизма в руководствах по Java как способ начать работу с потоками.Кроме того, ищите курсы колледжа в Интернете.Многие школы публикуют заметки в Интернете, поэтому с небольшим поиском вы часто можете найти высококачественный материал.
РЕДАКТИРОВАТЬ : описание разницы между ожиданием и блокировкой ввода / вывода
Прежде чем вы начнете читать это, убедитесь, что вы знакомы с тем, что такое поток и что такое процесс.Я даю объяснение в первых четырех абзацах этого SO ответа , и Википедия содержит более подробное объяснение (хотя и с меньшим историческим контекстом).
Каждая тема имеетодна очень важная часть информации: указатель инструкции (есть другие важные части информации, связанные с каждым потоком, но они не важны в настоящее время).Указатель инструкции - это поддерживаемый JVM указатель на выполняемую в настоящее время инструкцию байт-кода.Каждый раз, когда вы выполняете инструкцию (каждая инструкция является абстрактным представлением очень простой операции, такой как «вызов метода foo
для объекта x
), указатель инструкции перемещается вперед к некоторой« следующей инструкции ». Чтобы выполнить вашуВ программе JVM устанавливает указатель инструкций на начало main
и продолжает выполнять инструкции и перемещать указатель инструкций вперед до тех пор, пока программа не выйдет каким-либо образом.
A операция блокировки останавливает перемещение указателя команды вперед до тех пор, пока не произойдет какое-либо событие, заставляющее указатель инструкции снова двигаться вперед. Конечно, поток, который инициировал операцию блокировки, не может заставить это событие произойти, потому что указатель инструкции этого потока не движется вперед, то есть этот поток ничего не делает.
Сейчас существует множество различных видов операций блокировки. Один из них блокирует ввод / вывод. Например, если вы вызываете System.out.println
, метод println
не возвращается, пока текст не будет записан на консоль. В этом случае указатель инструкций останавливается где-то внутри System.out.println
, а операционная система сигнализирует о том, что поток просыпается при завершении печати на консоли. Таким образом, поток не должен начинать перемещать свой собственный указатель инструкций, но метод все еще возвращает сразу после записи текста в консоль. Итак, на очень высоком уровне:
- Тема 0 звонков
System.out.println("foo")
- Указатель инструкций потока 0 перестает двигаться, пока операционная система пишет «foo» в консоль
- Когда операционная система завершает запись в консоль, она уведомляет JVM, и JVM автоматически начинает перемещение указателя инструкций потока 0 снова. Все это происходит без необходимости думать об этом программисту, который пишет
System.out.println
.
Другой полностью отдельный вид операции блокировки заключен в методе Object.wait
. Всякий раз, когда поток вызывает Object.wait
, указатель инструкций этого потока перестает двигаться, но вместо того, чтобы операционная система снова начала перемещение указателя инструкций, другой поток выполняет свою работу. В этом случае не существует внешнего события, которое вызовет перезапуск указателя инструкций потока (как в случае блокирующего ввода-вывода), но является внутренним для программы событием. Как я уже сказал, другой поток снова начнет движение указателя инструкций, вызвав Object.notify
. Итак, на очень высоком уровне:
- Поток 0 вызывает
x.wait()
для некоторого объекта
- Указатель инструкции потока 0 перестает двигаться
- Поток 1 вызывает
x.notify()
для того же объекта x
- Указатель инструкции потока 0 снова начинает двигаться
- Поток 0 и поток 1 теперь выполняются одновременно
Обратите внимание, что для правильного написания кода ожидания / уведомления необходимо проделать гораздо больше работы - JVM и операционная система на этот раз не выполняют всю работу за вас. Они все еще выполняют большую часть работы за вас, но вам действительно нужно подумать о вызовах wait
и notify
и о том, как они позволяют вам общаться между потоками, реализовывать блокировки и многое другое.
Итак, в этой истории есть две морали. Во-первых, блокирующие ввод / вывод и wait
- совершенно разные звери. В обоих случаях поток блокируется, но в случае блокирования ввода-вывода поток автоматически активируется операционной системой, тогда как в случае wait
поток должен полагаться на другой поток, вызывающий notify
, чтобы проснись Во-вторых, параллельное программирование сложнее рассуждать, чем последовательное программирование. Примеры игрушек, которые я привел в этом ответе, на самом деле не соответствуют второй точке.