I have a Bank
class with a list of Account
. The bank has a transfer()
method to transfer a value from one account to another. The idea is to lock both the from
and to
accounts within a transfer.
To solve this issue I have the following code (please bear in mind that this is a very trivial example because it's just that, an example):
public class Account {
private int mBalance;
public Account() {
mBalance = 0;
}
public void withdraw(int value) {
mBalance -= value;
}
public void deposit(int value) {
mBalance += value;
}
}
public class Bank {
private List<Account> mAccounts;
private int mSlots;
public Bank(int slots) {
mAccounts = new ArrayList<Account>(Collections.nCopies(slots, new Account()));
mSlots = slots;
}
public void transfer(int fromId, int toId, int value) {
synchronized(mAccounts.get(fromId, toId)) {
synchronized(mAccounts.get(toId)) {
mAccounts.get(fromId).withdraw(value);
mAccounts.get(toId).deposit(value);
}
}
}
}
This works, but does not prevent deadlocks. To fix that, we need to change the synchronization to the following:
synchronized(mAccounts.get(Math.min(fromId, toId))) {
synchronized(mAccounts.get(Math.max(fromId, toId))) {
mAccounts.get(fromId).withdraw(value);
mAccounts.get(toId).deposit(value);
}
}
But the compiler warns me about nested synchronization blocks and I trust that that is a bad thing to do? Also, I'm not very fond of the max/min solution (I was not the one who came up with that idea) and I would like to avoid that if possible.
How would one fix those 2 problems above? If we could lock on more than one object, we would lock both the from
and to
account, but we can't do that (as far as I know). What's the solution then?