死锁问题说明


一、什么是死锁

多个进程竞争有限数量的资源时,当一个进程申请的资源被其他等待进程占用,那么该等待线程有可能再也无法改变状态,这种情况被称为死锁,一旦程序发生死锁,线程就会崩溃,无法执行后续工作。

二、死锁的三个典型情况

1.情况一

一个线程,使用一把锁,进行连续加锁两次,如果该锁是不可重入锁,就会死锁

2.情况二

两个线程t1,t2,使用两把锁,t1,t2先对进行自己加锁,再尝试获取对方

举个例子:现在有t1和t2两个人,在吃面条,都想要加辣椒和醋,其中t1拿到了辣椒,t2拿到了醋,t1对t2说你把醋给我,我用完了给你,t2对t1说你把辣椒给我,我用完了给你,如果两人均不想后退一步,那么就会僵持,就会出现死锁状态

        Object lajiao = new Object();
        Object cu = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (lajiao) {

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (cu) {
                    System.out.println("辣椒和醋都拿到了");
                }
            }
        });
        Thread t2 = new Thread(() -> {
            synchronized (cu) {

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (lajiao) {
                    System.out.println("辣椒和醋都拿到了");
                }
            }
        });
        t1.start();
        t2.start();

3.情况三

多个线程,使用多把锁,相当于情况二的一般情况

经典例子:哲学家就餐问题是一个经典的并发控制问题,它描述了一组哲学家围坐在一个圆桌旁,每个人都有一双筷子,他们需要同时拿起左右两边的筷子才能吃饭。

这个问题可以抽象为以下五个条件:

1.每个哲学家同时拿起左右两边的筷子;
2.如果一个哲学家左右两边的筷子都已经被其他哲学家拿走,那么这个哲学家就会等待;
3.当一个哲学家拿到左右两边的筷子后,他就会开始吃饭;
4.吃完饭后,哲学家会放下手中的筷子;
5.哲学家吃饭和思考的过程是交替进行的。

在这个问题中,可能会出现死锁的情况。当所有的哲学家同时拿起左边的筷子时,他们都在等待右边的筷子被释放。然而,由于没有哲学家释放右边的筷子,所以所有的哲学家都会陷入无限等待的状态,从而产生死锁。

在这里插入图片描述

三、可重入和不可重入

在情况一中我们说到不可重入锁的问题,现在我们就来说明,什么是可重入锁和不可重入锁。
简单而言,一个线程针对同一个对象,连续加锁两次,是否会有问题,如果没问题,就叫可重入,如果有问题,就叫不可重入

    public int count = 0;

    synchronized public void add() {
        synchronized (this) {
            count++;
        }
    }

在上图代码中,锁的对象是this,如果有线程调用add()方法,就会先对add()方法进行加锁操作,然后遇到了代码块,再次尝试加锁操作,当站在锁对象的角度来说,自己已经加锁了,被其他线程给占用,现这里的第二次加锁是否需要阻塞等待,但是这里特殊的是,这里的两次加锁都为同一个线程所为,如果允许上述操作,不阻塞等待,那么就是可重入锁,如果不允许上述操作,阻塞等待就是不可重入锁,并且该线程就会死锁

在Java中synchronized就是可重入锁,当遇到两次加锁时,会在锁对象中检查,当加锁线程和持有锁线程是同一个线程时,就会直接通过,否则进行阻塞

四、死锁的四个必要条件

1.互斥使用

当线程1拿到对象并加锁后,线程2必须等待

2.不可抢占

线程1拿到对象并加锁后,必须是线程1主动释放锁,线程2才能获取资源,线程2不能将对象强行获取

3.请求和保持

线程1获得对象A并加锁A之后,还想要请求另外一个对象B(该对象被其他线程添加锁B),并且锁A并没有释放,即并没有因为想要请求锁B而释放锁A

4.循环等待

线程1在获取A的时候,同时请求B的时候等待线程2释放B,线程2在获取B的时候,同时请求A的时候等待线程1释放A(类似上面的吃饭问题)

五、如何破除死锁

在上面的死锁的四个必要条件中,三个都是锁的基本特性,只有一个条件是跟代码结构有关的— 循环等待,所以破除死锁的办法就是打破循环等待,可以给锁添加编号,指定一个固定的顺序(从大到小,或从小到大)来加锁,当任意线程增加锁时,都让线程遵守上面的顺序,就可以破除循环,比如上图中的哲学家就餐问题中给每根筷子编号,并且先拿小编号,再拿大编号,就不会产生死锁
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
死锁是指在一个系统中,多个进程或线程因竞争共享资源而导致的互相等待,无法继续执行的情况。当多个进程或线程相互等待对方释放资源时,系统陷入了死锁状态。 在rt-thread中,为了解决死锁问题,我们可以采取以下措施: 1. 避免使用嵌套锁:在编写代码时,尽量避免在一个锁的范围内再次申请其他锁。如果需要多个锁,可以尝试使用锁的层次结构,确保锁的获取和释放的顺序一致。 2. 按序申请和释放资源:在多个线程或进程之间共享资源时,需要按照一定的顺序来申请和释放资源,避免发生循环依赖。这样可以避免死锁的产生。 3. 加锁时限:在rt-thread中,可以设置锁的持有时间,如果某个锁的持有时间超过一定阈值,可以主动释放锁,并记录错误信息。这样可以减少死锁的发生频率,提高系统的可靠性。 4. 死锁检测和恢复:在rt-thread中,可以实现死锁检测机制,当检测到死锁时,可以采取一些恢复措施,如释放所有锁,并通知相关线程或进程重新开始执行。这样可以及时解决死锁问题,保证系统的正常运行。 5. 合理设计并发结构:在系统设计阶段,可以合理划分资源的使用范围,减少竞争,避免死锁的发生。可以使用分布式锁、读写锁等机制,来避免资源竞争引起的死锁问题。 综上所述,在rt-thread中解决死锁问题可以通过避免嵌套锁、按序申请和释放资源、加锁时限、死锁检测和恢复以及合理设计并发结构等措施来实现。通过这些方法,可以有效预防和解决死锁问题,提高系统的可靠性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值