Банкомат

  • 9
  • Недоступна
Разберись, как работает программа Во время тестирования лог содержит следующее: ..... Добавляем 100, на счету 1100 Добавляем 100, на счету 1200 Тратим 1000, на счету 100 Недостаточно денег ..... Создан баг: При списании денег со счета теряются деньги Найти и исправить ошибку
Вы не можете решать эту задачу, т.к. не залогинены.
Комментарии (81)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий вы должны авторизоваться
TheDIP20 уровень, Киев
15 февраля, 17:54
Вообщем что я понял из разбора задачи, может кто-то тоже над этим думает. Обьект(статическая переменная), в нашем случае
static BankAccount account = new BankAccount("Amigo");
МОЖЕТ использоваться для вызова 1) синхронизированного 2) и не синхронизированного методов одновременно (разными потоками) Рефер на документацию: First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object. https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html Правда тут сказано про 2 синхронизированных метода, что они не могут использоваться, но я протестил и синхронизированный и не синхронизированный могут. Такие дела.
Alexander19 уровень, Москва
3 февраля, 15:45
Если я все правильно понял, то: 1. Гонка сигналов во время снятия/занесения денег на аккаунт 2. Из условий задачи деньги заносяться раз в секунду, а сниматься должны раз в 0.1 секунду. Соответственно запретить снятие чаще, чем раз в 0.1 секунду.
Mikhail Uchakov24 уровень, Санкт-Петербург
22 января, 09:31
Валидатор принимает решение только при изменении двух методов в классе BankAccount. Читайте внимательно требования к задаче.
Игорь22 уровень, Нижний Новгород
31 декабря 2018, 09:20
Не понятно почему не срабатывает синхронизация внутри метода withdraw:
public void withdraw(BigDecimal money) throws NotEnoughMoneyException {
     synchronized (balance) {
          BigDecimal newBalance = balance.subtract(money);
          if (newBalance.compareTo(BigDecimal.ZERO) < 0) throw new NotEnoughMoneyException();
          balance = newBalance;
          System.out.println("Тратим " + money + ", на счету " + balance);
     }
}
В таком варианте есть возможность вывода:
Тратим 100, на счету 400
Тратим 100, на счету 200
Тратим 100, на счету 200
Тратим 100, на счету 100
Тратим 100, на счету 0
Хотя и изменение переменной и ее вывод внутри одного блока synchronized. Приходится синхронизировать на уровне всего объекта - т.е. добавлять synchronized в сигнатуру метода:
public synchronized void withdraw(BigDecimal money)
Хотя никак не понимаю почему в первом варианте может быть такой баг - ведь у нас все нити используют один объект BankAccount и в нем изменение переменной и ее вывод неразделимы.
Pavlic Morozov (pashok09i)27 уровень, Екатеринбург
16 декабря 2018, 19:47
очень странно, ничего не менял, программа работала корректно, менял и сумму добавления и списания. Синхронизация видимых результатов не дала...
Vitaly Khan35 уровень
1 декабря 2018, 08:06
уменьшил время сна в addMoney до 500 мс и... убедился, что синхронизация методов в BankAccount не помогает убрать глюки! периодически проскакивает "Недостаточно денег" там, где этого быть не должно! я пробовал все виды синхронизации в методах deposit и withdraw, пробовал помечать balance, как volatile. но ВО ВСЕХ СЛУЧАЯХ программа иногда давала сбои.... (валидатор-то с первой попытки был удовлетворен...) UPDATE: разгадку нашел сам, она в моем комментарии немного ниже.
Vitaly Khan35 уровень
1 декабря 2018, 09:47
думал, что разобрался как работает синхронизация... но на этой задачи столкнулся c чем-то необъяснимым пока. просьба к остальным. попробуйте поставить задержку 500 мс в addMoney и погоняйте программу много раз. не у меня же одного такая проблема?... (картинка прилагается)
Dinara22 уровень, Москва
2 декабря 2018, 16:23
Да, у меня так же... Получается, во время выполнения метода deposit(), когда поток addMoney уже успел вывести на печать новый баланс
System.out.println("Добавляем " + money + ", на счету " + newBalance);
, но до выполнения команды
balance = newBalance;
, выполнение переключается на один из потоков spendThread, а баланс еще не обновился. Предполагаю, что проблема в этом. Т.к. объявление переменной balance как volatile не помогло.
Vitaly Khan35 уровень
3 декабря 2018, 02:17
не совсем так, Динара! я пробовал делать еще один вывод на экран в методе deposit уже ПОСЛЕ balance = newBalance; и было видно, что этот вывод происходит, а значит, и баланс обновлен! и уже ПОСЛЕ ЭТОГО вываливалось сообщение от другого потока, что недостаточно денег!
Vitaly Khan35 уровень
3 декабря 2018, 02:45
а вот теперь я понял, из-за чего это происходит! один поток заходит в withdraw, когда баланс равен 0. доходит до проверки условия и делает throw new NotEnoughMoneyException(); в этот момент он выходит из блока synchronized и снимает блокировку! еще до того, как он успеет обработать в методе run исключение, другой поток заходит в deposit и успешно кладет деньги на счет. только после этого происходит обработка исключения первым потоком! уфф))) разгадал) остается синхронизировать снятие денег в методе run. хотя это настолько замедляет выполнение, что у меня банковский счет начинает только из-за блока synchronized пополняться быстрее, чем расходоваться (при задержке 500 мс в addMoney).
vk20 уровень, Санкт-Петербург
12 декабря 2018, 21:44
F! А почему нельзя сделать синхронизацию только снаружи - на вызов метода account.deposit и на весь блок try
{
    account.withdraw("100");             //снимаем со счета
} catch (NotEnoughMoneyException e) {
    System.out.println("Недостаточно денег");
}
и не настраивать синхронизацию внутри аккаунта?
Игорь22 уровень, Нижний Новгород
31 декабря 2018, 10:00
Попробовал синхронизацию только снаружи:
while (!isStopped) {
     synchronized (account) {
          account.deposit("1000");
               try {
                    Thread.sleep(1000);
               } catch (InterruptedException e) {
                    break;
          }
     }
}
Получил такой вот вывод:
Добавляем 1000, на счету 1000
Добавляем 1000, на счету 2000
Добавляем 1000, на счету 3000
Добавляем 1000, на счету 4000
Добавляем 1000, на счету 5000
Тратим 100, на счету 4900
Тратим 100, на счету 4800
Тратим 100, на счету 4700
Получается, что нити, ответственные за пополнение, даже не успевают войти в соответствующие методы. А если синхронизировать внутри, то они туда заходят, натыкаются на блокировку, но по освобождению объекта - продолжают выполнение своих методов.
Игорь22 уровень, Нижний Новгород
31 декабря 2018, 10:24
А, ошибся... не надо в deposit в synchronized try-sleep заворачивать. Тогда все работает.
fleek25 уровень
23 ноября 2018, 16:04
данный пример в помощь https://www.youtube.com/watch?v=FPN7gadNT4A&list=PLnV3K-pmuXwg9S6YhNnWvOG3PXkSaVPsN
Игорь25 уровень
4 ноября 2018, 19:31
почему если запускаем еще одну нить addMoney.start() выводится IllegalThreadStateException и тратильщики денег тоже не запускаются?
Anonymous #37410529 уровень, Амстердам
13 ноября 2018, 14:54
Потому что ты дважды пытаешься запустить одну и ту же нить, а не ещё одну.
Photograph Pro20 уровень, Киев
4 октября 2018, 17:51
это прям один в один задача из Head First тема синхронизация потоков=))
Александр Толкачёв22 уровень, Санкт-Петербург
7 сентября 2018, 19:22
Для тех, кто хочет сам разобраться в программе, и не понимает где ошибка в исходном коде, добавьте второй аналогичный поток на добавление денег, и все станет ясно