public void transferMoney(Account fromAccount, Account toAccount, Amount amount) throws InsufficientFundsException {
synchronized (fromAccount) {
synchronized (toAccount) {
if (fromAccount.getBalance().compareTo(amount) < 0)
throw new InsufficientFundsException();
else {
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
}
}
Описание: Если со счета A на счет B перевести x денег, а со счета B на счет A – y, то при неудачном стечении обстоятельств, транзакция 1 займет монитор счета A, транзакция 2 займет монитор счета B. Результат – взаимная блокировка.
Я никак не могу понять: Как же второй трэд доберётся до второго synchronized, если первый synchronized уже был заблокирован первым трэдом?
Пересмотрел уже кучу лекций и перечитал про synchronized.
Ответ
Он не будет заблокирован. synchronized воспользуется intrinsic lock того объекта, который будет указан в самом synchronized, поэтому два потока могут войти во внешний synchronized при том условии, что они используют два разных объекта: де-факто, synchronized(lockObject) - это гарантия использования объекта только одним потоком, но не гарантия исполнения кода максимум одним потоком в любой момент времени. Для достижения дедлока в этом случае достаточно, чтобы ни один из потоков не смог войти во внутренний synchronized - и это условие выполняется, если используются те же два аккаунта, но в противоположных потоках:
Thread 1 | Thread 2
-------------------|-------------------
обычное состояние | обычное состояние
взял лок объекта А | взял лок объекта Б
ждет лок объекта Б | ждет лок объекта А
В этом случае прогресс невозможен, потому что для освобождения ресурса требуется прогресс любого из потоков, а это возможно только в случае освобождения ресурса.
Комментариев нет:
Отправить комментарий