Page 192 - 6571
P. 192
компілятори можуть бути джерелом для появи великої кількості
помилок під час таких нескладних операцій:
1. Комп’ютери з декількома процесорами можуть тимчасово
утримувати значення з пам’яті в регістрах або локальних кешах.
Як наслідок, потокам, що виконуються на різних процесорах,
можуть бути доступні різні значення в одній і тій же області
пам’яті.
2. Компілятори можуть змінювати порядок виконання
команд для досягнення максимальної продуктивності програми.
При цьому, результат виконання програми жодним чином не
міняється, але робиться припущення, що значення в пам’яті
змінюються тільки явними командами в коді. Проте, в
багатопотоковій програмі значення в пам’яті може бути змінено
виконанням будь-якого потоку.
Ключове слово volatile використовується для
встановлення неблокуючого режиму синхронізованого доступу
до поля класу. У випадку, коли поле класу оголошене із
модифікатором volatile, то компілятор і віртуальна машина
враховують той факт, що значення поля може бути паралельно
модифіковане в іншому потоці.
Припустимо, що у класі визначено поле done типу boolean,
значення якого встановлюється в одному потоці і зчитується в
іншому. Для синхронізації доступу до даного поля можна
використати механізм вбудованого блокування під час
проектування відповідного класу:
private boolean done;
public synchronized boolean isDone() { return done; }
public synchronized void setDone() { done = true; }
Проте, можливо використання вбудованого блокування є не
найкращою ідеєю. Адже методи isDone() та setDone() можуть
виявитися заблокованими, якщо інший потік заблокував об’єкт
відповідного класу. Інший варіант полягає у використанні для
поля done об’єкту типу Lock. Проте, у цьому випадку потрібно
написати багато рутинного коду, тому є зміст оголосити поле
done з модифікатором volatile як це зроблено нижче:
private volatile boolean done;
public boolean isDone() { return done; }
public void setDone() { done = true; }
191