Page 189 - 6571
P. 189
В даному випадку об’єкт lock створюється тільки для
використання вбудованого блокування, яке є у кожного об’єкта в
мові Java. Вбудованим блокуванням об’єкта іноді користуються
для реалізації додаткових атомарних операцій. Така практика
отримала назву клієнтського блокування. Розглянемо як приклад
клас Vector, методи якого є синхронізованими. А тепер
припустимо, що залишки на банківських рахунках зберігаються в
об’єкті типу Vector<Double>. Нижче наведена наївна реалізація
методу transfer().
public void transfer(Vector<Double> accounts, int from,
int to, int amount) { // помилка!
accounts.set(from, accounts.get(from) - amount);
accounts.set(to, accounts.get(to) + amount);
System.out.println();
}
Методи get() та set() визначені в класі Vector є
синхронізованими, проте, цілком можливо, що потік буде
призупинений на методі transfer() після завершення першого
виклику методу get(). Інший потік зможе записати будь-яке
інше значення на ту ж саму позицію. Тому, для захоплення
блокування можна використати наступний підхід:
public void transfer(Vector<Double> accounts, int from,
int to, int amount) {
synchronized (accounts) {
accounts.set(from, accounts.get(from) - amount);
accounts.set(to, accounts.get(to) + amount);
}
System.out.println();
}
Такий підхід цілком працездатний, але він повністю залежить
від того факту, що вбудоване блокування використовується в
класі Vector для всіх його методів. З цієї причини, клієнтське
блокування є досить ненадійним прийомом, і тому зазвичай воно
не рекомендується до застосування.
20.2 Керування міжпотоковими комунікаціями
У попередніх прикладах для блокування інших потоків від
асинхронного доступу до методів використовувалися неявні
188