java wait notify пример

Класс Object в Java содержит три final метода для взаимодействия потоков. Это методы wait() , notify() и notifyAll() . В этой статье мы расскажем что это за методы и как их эффективно использовать в многопоточных программах.

Поток, вызывающий эти методы на любом объекте, должен иметь так называемый монитор (механизм многопоточного доступа к объекту). Если же его нет, то будет брошено исключение java.lang.IllegalMonitorStateException .

Содержание

  1. Метод wait()
  2. Метод notify()
  3. Метод notifyAll()
  4. Методы wait() , notify() и notifyAll() . Практика

Метод wait()

У метода wait() есть три вариации. Один метод wait() бесконечно ждет другой поток, пока не будет вызван метод notify() или notifyAll() на объекте. Другие две вариации метода wait() ставят текущий поток в ожидание на определенное время. По истечении этого времени поток просыпается и продолжает работу.

Метод notify()

Вызов метод notify() пробуждает только один поток, после чего этот поток начинает выполнение. Если объект ожидают несколько потоков, то метод notify() разбудит только один из них. Выбор потока зависит от системной реализации управления потоками.

Метод notifyAll()

Метод notifyAll() пробуждает все потоки, хотя в какой последовательности они будут пробуждаться зависит от реализации ОС.

Методы wait() , notify() и notifyAll() . Практика

Давайте на примере посмотрим использование методов wait() , notify() и notifyAll() .

Ниже представлен Java Bean, на котором будут работать наши потоки:

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

Пример 1. Простой, но неправильный пример

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

Пример 2. Ожидание с помощью wait() и notifyAll()

Как видно из данного примера, отправляющий данные поток не циклиться, а вызывает метод wait() у нашего объекта-монитора. Напомню принцип работы синхронизации: как только первый поток попадает в блок synchronized, он становится владельцем объекта monitor и “блокирует” его. Никакой другой поток не сможет попасть в блок, т.е. стать владельцем этого объекта до тех пор, пока поток владелец не “отпустит” монитор, выйдя из блока синхронизации.

Важно! Методы wait(), notify() и notifyAll() должны обязательно находиться внутри блока synchronized, либо внутри synchronized-метода, иначе вы получите Exception. Но возникает вопрос, каким образом второй поток сможет попасть в блок синхронизации, чтобы вызвать notifyAll(), если первый в этот момент будет владельцем monitor’а и будет ждать notify() ? Все просто – как только поток достигает метода wait() он перестает быть владельцем монитора, блокировка снимается, а поток уходит в сон.

Потенциальные проблемы данного кода:
1. Возможна ситуация, когда первый поток подготовил данные раньше, чем второй начал ждать его. В этом случае notifyAll() никого не разбудит и соответственно, когда второй поток дойдет до wait(), процесс остановится навсегда.
2. Существуют в природе так называемые “ложные пробуждения”, когда поток просыпается сам по себе, без всяких на то причин.

Чтобы избежать обе проблемы, усовершенствуем наш код.
Пример 3. Ожидание в цикле.

В интернете нашел пример использования wait и notify :

В реализации методов get и put есть метод notify , который будит потоки, спящие на мониторе store , и это происходит каждый раз при их вызове, т.е. когда поток покупатель купил товар и вызвал notify в методе get , поток производитель на этом же мониторе УЖЕ ПРОСНУЛСЯ И ЖДЕТ ОСВОБОЖДЕНИЯ МОНИТОРА?

Так как вызывается несколько раз notify , потому что вызывается несколько раз get и put , столько же раз происходит пробуждение потоков на мониторе, получается поток уже проснулся, и его еще 2 раза будят — как это с точки зрения правильности?

Источник: computermaker.info

Техника и Гаджеты
Добавить комментарий