多线程银行转账死锁问题
在转账操作中,一致性必须要保证的,转账的前后,各个账户的金额必须符合算术一致性,如果在转账方法上直接加锁,那相当于整个转账过程都是单线程的,并发和性能不高,如果针对账户加锁,会存在A->B,B->A之间死锁问题**
- 账户信息
public class Account {
private String id;
private String name;// 账号
private int balance;// 资金总额
public Account(String id,String name, int balance) {
this.id = id;
this.name = name;
this.balance = balance;
}
public String getName() {
return name;
}
public int getBalance() {
return balance;
}
public String getId() {
return id;
}
public void debit(int amount) {// 更新转出方余额
this.balance -= amount;
}
public void credbit(int amount) {// 更新转入方余额
this.balance += amount;
}
- 转账实现,根据对账户进行排序加锁
public interface Transfer {
public void transfer(Account from, Account to, int amount);
}
public class TransferImpl implements Transfer {
public void transfer(Account fromAccount, Account toAccount, int amount) {
int fromHashCode = fromAccount.getId().hashCode();
int toHashCode = toAccount.getId().hashCode();
Account left = fromAccount;
Account right = toAccount;
// 按从小到大排序
if (fromHashCode > toHashCode) {
left = toAccount;
right = fromAccount;
}
synchronized (left) {
// 获取right的锁
synchronized (right) {
// 两个锁都获取到执行操作
if (fromAccount.getBalance() <= amount) {
System.out.println(Thread.currentThread().getName()+"->"+fromAccount.getName() + "账户余额不足,无法进行转账"+" 余额:"+fromAccount.getBalance()+"<"+amount);
return;
}
fromAccount.debit(amount);
System.err.println(Thread.currentThread().getName()+"->"+fromAccount.getName() + "**转出**" + amount+"**剩余**" + fromAccount.getBalance());
toAccount.credbit(amount);
System.out.println(Thread.currentThread().getName()+"->"+toAccount.getName() + "--被【"+fromAccount.getName()+"】转入--" + amount+"--剩余--" + toAccount.getBalance());
}
}
}
public static void main(String[] args) {
List<Account> allAccount = new ArrayList<Account>();
allAccount.add(new Account("1","张三", 100));
allAccount.add(new Account("2","李四", 100));
allAccount.add(new Account("3","小梅", 100));
allAccount.add(new Account("4","小明", 100));
for (int i = 0; i < 10; i++) {
int findex = ThreadLocalRandom.current().nextInt(4);
int toindex = ThreadLocalRandom.current().nextInt(4);
int amount = ThreadLocalRandom.current().nextInt(50,80);
if (findex != toindex) {
Runnable r = new TransferThread(allAccount.get(findex), allAccount.get(toindex), amount,allAccount);
new Thread(r).start();
}
}
}
public static class TransferThread implements Runnable {
private Account fromAccount;
private Account toAccount;
private int amount;
private List<Account> allAccount;
public TransferThread(Account fromAccount, Account toAccount, int amount,List<Account> allAccount) {
this.fromAccount = fromAccount;
this.toAccount = toAccount;
this.amount = amount;
this.allAccount = allAccount;
}
public void run() {
new TransferImpl().transfer(fromAccount, toAccount, amount);
int total = 0;
for(Account account:allAccount) {
total = total + account.getBalance();
}
System.err.println(Thread.currentThread().getName()+"-> total:" + total);
}
}
}
- 执行结果
Thread-0->李四转出58剩余42
Thread-0->张三–被【李四】转入–58–剩余–158
Thread-3->李四账户余额不足,无法进行转账 余额:42<66
Thread-2->李四–被【小明】转入–74–剩余–116
Thread-1->李四–被【张三】转入–67–剩余–183
Thread-2->小明转出74剩余26
Thread-1->张三转出67剩余91
Thread-3-> total:400
Thread-0-> total:400
Thread-2-> total:400
Thread-1-> total:400