Page 179 - 6571
P. 179

double amount = maxAmount * Math.random();
                        bank.transfer(fromAccount, toAccount, amount);
                        Thread.sleep((int) (DELAY * Math.random()));
                      } catch (InterruptedException e) { }

                    }
                  }

                  Під час виконання даної програми, невідомо, яка саме сума
            знаходиться  на  будь-якому  банківському  рахунку  в  довільний
            момент часу. Але в той же час відомо, що загальна сума грошей

            за  всіма  рахунками  повинна  залишатися  незмінною,  оскільки
            гроші  тільки  переводяться  з  одного  рахунку  на  інший,  але  не
            знімаються  остаточно.  Наприкінці  кожної  транзакції  метод
            transfer()  заново  обчислює  кінцеву  суму  на  рахунках  і

            виводить її. При цьому програма працює у вічному циклі.
                  Запустивши  програму  на  виконання,  можна  виявити,  що
            через  деякий  час  загальний  баланс  все-таки  зміниться.  Це

            пов’язано з тим, що два потоки намагаються одночасно оновити
            один  і  той  же  рахунок.  Припустимо,  що  два  потоки  одночасно
            намагаються виконати наступну операцію:

                  accounts[to] = accounts[to] + amount;

                  Проте  дана  операція  не  є  атомарною  і  може  бути  виконана
            поетапно наступним чином:

                  1) завантажити значення елемента accounts[to]з масиву в
            регістр;
                  2) додати до нього значення змінної amount;

                  3) записати  результат  назад  в  масив  на  місце  елемента
            accounts[to].
                  Представимо, що в першому потоці виконуються операції 1

            та 2, після чого його виконання призупиняється. Припустимо, що
            другий  потік  виходить  із  стану  очікування  і  оновлює  той  же
            самий  елемент  у  масиві  accounts.  Потім  виходить  із  стану

            очікування перший потік і виконує операцію під номером 3. Дана
            дія затирає зміни, що бути зроблені в другому потоці. У підсумку
            загальний баланс виявляється підрахованим невірно.

                  Суть  розглянутої  проблеми  полягає  в  тому,  що  виконання
            методу  transfer()  може  бути  перервано  на  півдорозі  до
            завершення  його  виконання.  Для  того  щоб  стан  об’єкта

            банківського  рахунку  не  був  порушений,  потрібно  гарантувати

                                                        178
   174   175   176   177   178   179   180   181   182   183   184