Page 191 - 6571
P. 191
метод wait() зазвичай перебуває в стані очікування до того
часу, поки не буде викликаний метод notify() або
notifyAll(), проте існує ймовірність, що в дуже рідкісних
випадках призупинений потік може бути поновлений
підробленим сигналом. Через цю малоймовірну можливості
Oracle рекомендує виконувати виклики методу wait() всередині
циклу, що перевіряє умову очікування потоку. У наведеному
нижче прикладі продемонстровано саме такий підхід.
class Bank {
private double[] accounts;
public synchronized void transfer(int from, int to,
int amount) throws InterruptedException {
while (accounts[from] < amount)
wait(); // очікувати згідно умови
accounts[from] = accounts[from] - amount;
accounts[to] = accounts[to] + amount;
// повідомити всі потоки, що очікують згідно умови
notifyAll();
}
public synchronized double getTotalBalance() {...}
}
Метод wait() переводить потік в стан очікування, а методи
notifyAll() / notify() розблокують призупинені потоки.
Виклик методу wait() або notifyAll() рівнозначний
наступному коду:
buildConditional.await();
buildConditional.signalAll();
Отже, використання ключового слова synchronized
дозволяє суттєво скоротити вихідний код програми. В даному
випадку, блокування керує потоками, які намагаються увійти в
метод synchronized, а умова керує потоками, що викликали
метод wait().
Поля та змінні типу volatile. Часто для захисту одного
або двох полів класу від асинхронного доступу потрібно
синхронізувати доступ до усього об’єкта, що є занадто затратною
операцією в плані розподілу процесорного часу. Зрештою, що
може статися поганого під час асинхронних операцій запису або
зчитування поля екземпляру класу? На жаль, сучасні процесори і
190