— Привет, Амиго!
— Привет, Риша!
— Я познакомлю тебя с методами wait, notify, notifyAll класса Object.
Сегодня мы просто ознакомимся с ними, но потом еще раз вернемся и уже выделим на это больше времени.
— Хорошо.
— Эти методы были придуманы как часть механизма синхронизации нитей.
Напомню, что в Java есть встроенный механизм управления доступом к общим ресурсам (объектам) из разных нитей. Нить может объявить какой-нибудь объект занятым, и другие нити будут вынуждены ждать, пока занятый объект не освободится.
— Я помню, это делается с помощью ключевого слова synchronized.
— Правильно. Обычно такой код выглядит примерно так:
public void print()
{
Object monitor = getMonitor();
synchronized(monitor)
{
System.out.println("text");
}
}
Помнишь, как это работает?
— Ага. Если две нити одновременно вызовут метод print(), то одна из них войдет в блок, помеченный synchronized, и заблокирует monitor, поэтому вторая нить будет ждать, пока монитор не освободится.
— Правильно. Как только нить входит в блок, помеченный synchronized, то объект-монитор помечается как занятый, и другие нити будут вынуждены ждать освобождения объекта-монитора. Один и тот же объект-монитор может использоваться в различных частях программы.
— Кстати, почему – монитор?
— Монитором принято называть объект, который хранит состояние занят/свободен.
Вот тут и вступают в дело методы wait и notify.
Собственно, методов как таковых всего два. Остальные – это лишь модификации этих методов.
Теперь разберемся, что же такое метод wait и зачем он нужен.
Иногда в программе может оказаться такая ситуация, что нить вошла в блок кода synchronized, заблокировала монитор и не может работать дальше, т.к. каких-то данных еще не хватает: например, файл который она должна обработать еще не загрузился или что-нибудь в таком духе.
Мы же можем просто подождать, когда файл скачается. Можно просто в цикле проверять – если файл еще не скачался – спать, например, секунду и опять проверять и т.д.
Примерно так:
while(!file.isDownloaded())
{
Thread.sleep(1000);
}
processFile(file);
Но в нашем случае такое ожидание слишком дорого. Т.к. наша нить заблокировала монитор, то другие нити вынуждены тоже ждать, хотя их данные для работы могут быть уже готовы.
Для решения этой проблемы и был придуман метод wait(). Вызов этого метода приводит к тому, что нить освобождает монитор и «становится на паузу».
Метод wait можно вызвать у объекта-монитора и только тогда, когда это монитор занят – т.е. внутри блока synchronized. При этом нить временно прекращает работу, а монитор освобождается, чтобы им могли воспользоваться другие нити.
Часто встречаются ситуации, когда в блок synchronized зашла нить, вызвала там wait, освободила монитор.
Затем туда вошла вторая нить и тоже стала на паузу, затем третья и так далее.
— А как же нить снимется с паузы?
— Для этого есть второй метод – notify.
Методы notify/notifyAll можно вызвать у объекта-монитора и только, когда этот монитор занят – т.е. внутри блока synchronized. Метод notifyAll снимает с паузы все нити, которые стали на паузу с помощью данного объекта-монитора.
Метод notify «размораживает» одну случайную нить, метод notifyAll – все «замороженные» нити данного монитора.
— Очень интересно, спасибо Риша.
— Рад, что тебе нравится, Амиго!
Есть еще модификации метода wait():
Метод wait() | Пояснение |
---|---|
|
Нить «замерзает», но через переданное количество миллисекунд автоматически «размораживается». |
|
Нить «замерзает», но через переданное количество миллисекунд и наносекунд автоматически «размораживается». |
Это, как еще говорят, wait с таймаутом. Метод работает как обычный wait, но если указанное время прошло, а нить никто не снял с паузы – она активируется сама.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ