多线程学习(四)之 “死锁”

本文深入探讨了多线程编程中死锁和活锁的概念,通过实例代码展示了这两种现象的产生原因,并提供了有效的解决方案。文章还详细解释了死锁产生的四个必要条件,并介绍了如何通过代码设计避免这些问题。

上一篇
多线程学习(三)之线程安全性背后的本质–volatile关键字

死锁

死锁: 一组互相竞争资源的线程因互相等待,导致“永久”阻塞的现象

死锁的例子

public class TransferAccount implements  Runnable{

    private Account fromAccount; //转出账户
    private Account toAccount; //转入账户
    private 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){
            try {
                synchronized (fromAccount) {
                    synchronized (toAccount) {
                        if (fromAccount.getBalance() >= amount) {
                            fromAccount.debit(amount);
                            toAccount.credit(amount);
                        }
                    }
                }
                //转出账户的余额
                System.out.println(fromAccount.getAccountName() + "->" + fromAccount.getBalance());
                //转入账户的余额
                System.out.println(toAccount.getAccountName() + "->" + toAccount.getBalance());
            }
        }
    }

    public static void main(String[] args) {
        Account fromAccount=new Account("张三",100000);
        Account toAccount=new Account("李四",300000);
        
        Thread a =new Thread(new TransferAccount(fromAccount,toAccount,10));
        Thread b=new Thread(new TransferAccount(toAccount,fromAccount,30));

        a.start();
        b.start();
    }
}

活锁

活锁:活锁指的是任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试—>失败—>尝试—>失败的过程。处于活锁的实体是在不断的改变状态,活锁有可能自行解开

死锁发生的条件

这四个条件同时满足,就会产生死锁。

  • 互斥:共享资源 X 和 Y 只能被一个线程占用;(该条件不可被破坏)
  • 占有且等待线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X;
  • 不可抢占:其他线程不能强行抢占线程 T1 占有的资源;
  • 循环等待:线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待。

如何解决死锁问题

按照前面说的四个死锁的发生条件,我们只需要破坏其中一个,就可以避免死锁的产生。其中,互斥这个条件我们没有办法破坏,因为我们用锁的目的就是互斥,其他三个条件都有办法可以破坏

  • 对于 “占用且等待” 这个条件,我们可以一次性申请所有的资源,这样就不存在等待了。
public class Allocator {

    private List<Object> list=new ArrayList<>();
    synchronized  boolean apply(Object from,Object to){
        if(list.contains(from)||list.contains(to)){
            //有任意一个锁的时候,返回false
            return false;
        }
        // 一次性获取两把锁
        list.add(from);
        list.add(to);
        return true;
    }

    synchronized void free(Object from,Object to){
        list.remove(from);
        list.remove(to);
    }
    
}
  • 对于 “不可抢占” 这个条件,占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源,这样不可抢占这个条件就破坏掉了。
public class TransferAccount02 implements  Runnable{

    private Account fromAccount; //转出账户
    private Account toAccount; //转入账户
    private int amount;
    Lock fromLock=new ReentrantLock();
    Lock toLock=new ReentrantLock();

    public TransferAccount02(Account fromAccount, Account toAccount, int amount) {
        this.fromAccount = fromAccount;
        this.toAccount = toAccount;
        this.amount = amount;
    }


    @Override
    public void run() {
        while(true){
            if (fromLock.tryLock()) { //返回true和false
                if (toLock.tryLock()) {//返回true和false
                    if (fromAccount.getBalance() >= amount) {
                        fromAccount.debit(amount);
                        toAccount.credit(amount);
                    }
                }
            }
            //转出账户的余额
            System.out.println(fromAccount.getAccountName() + "->" + fromAccount.getBalance());
            //转入账户的余额
            System.out.println(toAccount.getAccountName() + "->" + toAccount.getBalance());
        }
    }

    public static void main(String[] args) {
        Account fromAccount=new Account("张三",100000);
        Account toAccount=new Account("李四",300000);
        Thread a =new Thread(new TransferAccount02(fromAccount,toAccount,10));
        Thread b=new Thread(new TransferAccount02(toAccount,fromAccount,30));

        a.start();
        b.start();
    }
}
  • 对于 “循环等待” 这个条件,可以靠按序申请资源来预防。所谓按序申请,是指资源是有线性顺序的,申请的时候可以先申请资源序号小的,再申请资源序号大的,这样线性化后自然就不存在循环了。(通过锁对象的hashCode来计算)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值