70 lines
1.6 KiB
Java
70 lines
1.6 KiB
Java
package dev.banking.model;
|
|
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
|
|
public class BankAccount {
|
|
|
|
private final int accountId;
|
|
private long balance;
|
|
private final ReentrantLock lock = new ReentrantLock();
|
|
|
|
public BankAccount(int accountId, long initialBalance) {
|
|
this.accountId = accountId;
|
|
this.balance = initialBalance;
|
|
}
|
|
|
|
public int getAccountId() {
|
|
return accountId;
|
|
}
|
|
|
|
public long getBalance() {
|
|
lock.lock();
|
|
try {
|
|
return balance;
|
|
} finally {
|
|
lock.unlock();
|
|
}
|
|
}
|
|
|
|
public void deposit(long amount) {
|
|
lock.lock();
|
|
try {
|
|
balance += amount;
|
|
} finally {
|
|
lock.unlock();
|
|
}
|
|
}
|
|
|
|
public void withdraw(long amount) {
|
|
lock.lock();
|
|
try {
|
|
balance -= amount;
|
|
} finally {
|
|
lock.unlock();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Lock ordering by accountId prevents deadlock:
|
|
* both A->B and B->A transfers always acquire the
|
|
* lower-id lock first, so no circular wait can form.
|
|
*/
|
|
public void transfer(BankAccount target, long amount) {
|
|
BankAccount first = this.accountId < target.accountId ? this : target;
|
|
BankAccount second = this.accountId < target.accountId ? target : this;
|
|
|
|
first.lock.lock();
|
|
try {
|
|
second.lock.lock();
|
|
try {
|
|
this.balance -= amount;
|
|
target.balance += amount;
|
|
} finally {
|
|
second.lock.unlock();
|
|
}
|
|
} finally {
|
|
first.lock.unlock();
|
|
}
|
|
}
|
|
}
|