Я внедряю параллельную банковскую систему, в которой все операции могут выполняться одновременно. Я реализовал поточно-безопасный метод transferMoney
, который переводит amount
со счета from
в to
.
transferMoney
реализован со следующим кодом:
public boolean transferMoney(Account from, Account to, int amount) {
if (from.getId() == to.getId()){
return false;
}else if(from.getId() < to.getId()) {
synchronized(to) {
synchronized(from) {
if(from.getBalance() >= amount) {
from.setBalance(from.getBalance()-amount);
to.setBalance(to.getBalance()+amount);
}else {
return false;
}
}
}
}else {
synchronized(from) {
synchronized(to) {
if(from.getBalance() >= amount) {
from.setBalance(from.getBalance()-amount);
to.setBalance(to.getBalance()+amount);
}else {
return false;
}
}
}
}
return true;
}
Для предотвращения взаимных блокировок я указал, что блокировки всегда устанавливаются в одном и том же порядке. Чтобы убедиться, что замки получены в том же порядке, я использую уникальный ID
из Account
.
Кроме того, я реализовал метод, который суммирует общую сумму денег в банке со следующим кодом:
public int sumAccounts(List<Account> accounts) {
AtomicInteger sum = new AtomicInteger();
synchronized(Account.class) {
for (Account a : accounts) {
sum.getAndAdd(a.getBalance());
}
}
return sum.intValue();
}
Задача
Когда я запускаю sumAccounts()
одновременно с transferMoney()
, я получу в банке больше (иногда меньше) денег , даже если деньги не были добавлены. Из моего понимания, если я блокирую все Account
объекты с помощью synchronized(Account.class)
, я не должен получить правильную сумму банка, поскольку я блокирую выполнение transferMoney()
?
Что я пробовал так далеко
Я пробовал следующие вещи:
- синхронизация
Account.class
как указано выше (не работает)
- синхронизация конкретной учетной записи в цикле
for each
(но, конечно, это не безопасно для потоков, поскольку транзакции происходят одновременно)
- синхронизация обоих методов через объект
ReentrantLock
. Это работает, но требует огромного снижения производительности (занимает в три раза больше, чем последовательный код)
- синхронизация обоих методов на уровне класса. Это также работает, но опять-таки занимает в три раза больше времени, чем последовательное выполнение операций.
Разве блокировка на Account.class
не должна предотвращать дальнейшие transferMoney()
казни? Если нет, как я могу решить эту проблему?
Edit:
Код для getBalance()
:
public int getBalance() {
return balance;
}