死锁---转账问题

死锁—转账问题

/*
* 需求:简单的银行转账,它将资金从一个账户转到另一个账户
* 在开始转账之前,需要获得两个Account的锁,以确保以原子的方式更新账户中的余额,且不能破坏不可变的条件,如账户的余额不能为负数
* 
*/
 
/*账户类*/
class Account{
    private String accountName;//账号
    private int balance;//资金总额
    public Account(String accountName,int balance){
        this.accountName = accountName;
        this.balance = balance;
    }
    public String getAccountName() {//获取账号
        return accountName;
    }
    public int getBalance() {//获取账号余额
        return balance;
    }
 
    public void debit(int amount){//更新转出方余额 
        this.balance -= amount;
    }
 
    public void credbit(int amount){//更新转入方余额
        this.balance += amount;
    }
}
 
 
class TransferAccount implements Runnable{
    //转出账户
    public Account fromAccount;
    //转入账户
    public Account toAccount;
    //转出金额
    public int amount;
    
    public TransferAccount(Account fromAccount,Account toAccount,int amount){
        this.fromAccount = fromAccount;
        this.toAccount = toAccount;
        this.amount = amount;
    }
    @Override
    public void run(){
        while(true){
            //获取fromAccount对象的锁
            synchronized(fromAccount){
                //获取toAccount对象的锁
                synchronized(toAccount){
                    //转账进行的条件:判断转出账户的余额是否大于0
                    if(fromAccount.getBalance() <= 0){
                        System.out.println(fromAccount.getAccountName() + "账户余额不足,无法进行转账");
                        return;
                    }else{
                        //更新转出账户的余额:-
                        fromAccount.debit(amount);
                        //更新转入账户的余额:+
                        toAccount.credbit(amount);
                    }
                }
            }
            //打印转出账户的余额
            System.out.println(fromAccount.getAccountName() + "......" + fromAccount.getBalance());
            //打印转入账户的余额
            System.out.println(toAccount.getAccountName() + "---" + toAccount.getBalance());
        }
    }
}
 
public class BankTransfer {
    public static void main(String[] args) {
        Account fromAccount = new Account("张三",100000);
        Account toAccount = new Account("李四",200000);
 
        Thread a = new Thread(new TransferAccount(fromAccount,toAccount,1));
        Thread b = new Thread(new TransferAccount(toAccount,fromAccount,2));
 
        a.start();
        b.start();
    }
}

死锁原因

占有且等待: 一次性申请所有的资源就可以解决
不可抢占: 占用部分资源后获取不到后续资源就释放掉前面获取的资源,就可以解决
循环等待: 按照序号申请资源来预防,也就是说给每个资源标记一个序号,没次加锁的时候都先获取资源序号小的,这样有顺序就不会出现循环等待

破坏“占用且等待”
只需要同时申请资源就可以,同时申请这个操作是一个临界区,需要一个Java类来管理这个临界区,也就是定义一个角色,这个角色的两个重要功能就是同时申请资源apply()和同时释放资源free(),并且这个类是单例的.其实本质就是设置一个管理员,只有管理员有权限去分配资源,其他普通用户只能去管理员那取资源,一个人操作就不会产生死锁了.
破坏不可抢占条件
这个的核心是释放掉已占有的资源,这个synchronized是做不到,因为synchronized申请资源的时候如果申请不到就直接进入阻塞,阻塞状态啥也干不了.
这个时候就需要java.util.concurrent包下提供的Lock,这个等学到的时候再总结.
破坏循环等待条件
确定一个加锁的顺序

class TransferAccount implements Runnable{
    //转出账户
    public Account fromAccount;
    //转入账户
    public Account toAccount;
    //转出金额
    public int amount;
 
    public TransferAccount(Account fromAccount,Account toAccount,int amount){
        this.fromAccount = fromAccount;
        this.toAccount = toAccount;
        this.amount = amount;
    }
    @Override
    public void run(){
        Account left =null;
        Account right = null;
        if (fromAccount.hashCode() > toAccount.hashCode()){
            left = toAccount;
            right =fromAccount;
        }
        while(true){
            //先获取left对象的锁
            synchronized(left){
                //再获取right对象的锁
                synchronized(right){
                    //转账进行的条件:判断转出账户的余额是否大于0
                    if(fromAccount.getBalance() <= 0){
                        System.out.println(fromAccount.getAccountName() + "账户余额不足,无法进行转账");
                        return;
                    }else{
                        //更新转出账户的余额:-
                        fromAccount.debit(amount);
                        //更新转入账户的余额:+
                        toAccount.credbit(amount);
                    }
                }
            }
            //打印转出账户的余额
            System.out.println(fromAccount.getAccountName() + "......" + fromAccount.getBalance());
            //打印转入账户的余额
            System.out.println(toAccount.getAccountName() + "---" + toAccount.getBalance());
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值