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.  Компілятори  можуть  змінювати  порядок  виконання  команд  для
               досягнення  максимальної  продуктивностіпрограми.  При  цьому,  результат
               виконання програми жодним чином не міняється, але робиться припущення,
               що значення в пам’яті змінюються тільки явними командами в коді. Проте, в
               багатопотоковій програмі значення в пам’яті може бути змінено виконанням
               будь-якого потоку.
   146   147   148   149   150   151   152   153   154   155   156