Page 151 - 4868
P. 151
149 Ошибка! Стиль не определен.
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. Часто для захисту одного або двох полів
класу від асинхронного доступу потрібно синхронізувати доступ до усього
об’єкта, що є занадто затратною операцією в плані розподілу процесорного
часу. Зрештою, що може статися поганого під час асинхронних операцій
запису або зчитування поля екземпляру класу? На жаль, сучасні процесори і
компілятори можуть бути джерелом для появи великої кількості помилок під
час таких нескладних операцій:
1. Комп’ютери з декількома процесорами можуть тимчасово утримувати
значення з пам’яті в регістрах або локальних кешах. Як наслідок, потокам,
що виконуються на різних процесорах, можуть бути доступні різні значення
в одній і тій же області пам’яті.
2. Компілятори можуть змінювати порядок виконання команд для
досягнення максимальної продуктивностіпрограми. При цьому, результат
виконання програми жодним чином не міняється, але робиться припущення,
що значення в пам’яті змінюються тільки явними командами в коді. Проте, в
багатопотоковій програмі значення в пам’яті може бути змінено виконанням
будь-якого потоку.