Page 144 - 4868
P. 144

Ошибка! Стиль не определен.                                                              142

               заблокованим,  оскільки  кожен  із  них  працює  з  своїм  екземпляром  класу
               Bank.
                     Описаний вище вид блокування називається реентерабельним, оскільки
               потік  може  повторно  захоплювати  блокування,  яким  він  уже  володіє.  Для
               кожного  блокування  передбачений  лічильник  захоплень,  що  відслідковує
               вкладені  виклики  методу  lock().  Кожен  виклик  методу  lock()  в  потоці

               повинен супроводжуватися подальшим викликом методу unlock(), з метою
               зняття блокування. Завдяки такому підходу, із захищеного блоку коду існує
               можливість  викликати  інші  методи,  що  використовують  те  ж  саме
               блокування.
                     Досить  часто  потік,  що  увійшов  в  критичну  секцію  коду  не  може
               продовжити свого виконання, оскільки не була задоволена певна умова. Тому
               для керування потоками, що захопили блокування, але не можуть виконати
               жодних  дій,  використовуються  умовні  об’єкти.  Також,  згідно  певних
               історичних причин умовні об’єкти нерідко називаються умовними змінними.
                     Спробуємо  удосконалити  розглянуту  раніше  програму  імітацію  банку.
               Не будемо виконувати переміщення коштів з рахунку, якщо на ньому не має
               достатньої  суми,  щоб  покрити  витрати  на  переказ.  Проте  код,  що
               представлено нижче не зовсім підходить для перевірки даної умови:
                     if (bank.getBalance(from) >= amount)
                       bank.transfer(from, to, amount);

                     Адже цілком ймовірно, що поточний потік призупинить своє виконання
               в проміжку між  успішним виконанням  перевірки  умови  і  викликом методу
               transfer():

                     if (bank.getBalance(from) >= amount)
                       // потік може призупинити своє виконання в цьому місці коду
                       bank.transfer(from, to, amount);
                     На  момент  відновлення  роботи  потоку  залишок  на  рахунку  може
               змінитися,  тобто  стати  меншим  дозволеної  межі.  Тому  необхідно  певним
               чином  гарантувати,  що  жодний  інший  потік  не  зможе  змінити  залишок  на
               рахунку  між  його  перевіркою  і  переказом  коштів.  Для  цього  потрібно
               захистити  як  перевірку  залишку  на  рахунку,  так  і  сам  переказ  коштів,  що
               можна зробити за допомогою наступного рішення:

                     public void transfer(int from, int to, int amount) {
                       bankLock.lock();
                       try {
                         while (accounts[from] < amount) {
                           // очікувати
                         }
                         // перевести кошти
                       } finally {
                         bankLock.unlock();
                       }
                     }
   139   140   141   142   143   144   145   146   147   148   149