— У цій лекції поговоримо про методи 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, але якщо вказаний час сплив, а потік ніхто не зняв з паузи – він активується сам.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ