Зачем нужны вложенные блоки синхронизации?
Подскажите, пожалуйста, зачем здесь использованы вложенные блоки синхронизации? И почему бы просто не добавить synchronized в сигнатуру метода? Разницу, точнее, я понимаю: в одном случае синхронизация по одному объекту, во втором - сразу по двум, но зачем? Вроде где-то что-то нашла про то, что таким образом пытаются избежать deadlock, но абсолютно не понимаю как это работает.
public void transfer(Account source, Account target, double amount) {
synchronized (sourceLock) {
synchronized (targetLock) {
source.setBalance(source.getBalance() - amount);
target.setBalance(target.getBalance() + amount);
}
}
}
Ответы (1 шт):
Не понятно, откуда берутся объекты sourceLock и targetLock.
Если это просто два глобальных статических объекта, то никакой разницы между блокировкой на методе и тем, как написано.
А вот если они как-то вычисляются в зависимости от счетов source и target, тогда разница есть и скорее всего код содержит ошибку, как указали в комментариях.
И почему бы просто не добавить synchronized в сигнатуру метода?
В таком случае банк сможет проводить только одну транзакцию в момент времени, даже если счета не связаны между собой
A->B и C->D не могут проводиться одновременно, потому что лок на методе.
во втором - сразу по двум, но зачем?
В том виде, как написано сейчас, A->B и C->D могут проводиться одновременно.
Но имплементация содержит ошибку, потому что создаёт возможность дедлока при одновременно выполнении A->B и B->A, т.е. один поток заблокирует А, другой B, и они будут ждать друг друга.
Чтобы исправить это, надо всегда блокировки накладывать в одном и том же порядке. В данном случае можно было бы упорядочить по номеру счёта
public void transfer(Account source, Account target, double amount) {
Object firstLock,secondLock;
if (source.getAccNum() < target.getAccNum()) {
firstLock= sourceLock;
secondLock= targetLock;
} else {
firstLock= targetLock;
secondLock= sourceLock;
}
synchronized (firstLock) {
synchronized (secondLock) {
source.setBalance(source.getBalance() - amount);
target.setBalance(target.getBalance() + amount);
}
}
}
Теперь не важно направление перевода (A->B или B->A), блокировки всегда будут накладываться в одном порядке A,B и дедлок не возникнет.