JavaEE——死锁(看图轻松理解哲学家就餐问题)

本文深入探讨了JavaEE中的死锁问题,包括死锁的定义、常见场景及如何避免。通过分析哲学家就餐问题,展示了死锁的产生条件,并提出了通过加锁顺序避免循环等待的解决方法。此外,还提及了银行家算法作为另一种防止死锁的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JavaEE传送门

JavaEE

JavaEE——JUC

JavaEE——线程安全的集合类



死锁

1. 死锁是什么

多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。简单来说就是: 死锁, 一个线程加上锁之后, 解不开了, 僵住了.

场景1: 一个线程, 一把锁,

线程连续加锁两次, 如果这个锁是不可重入锁, 就会变成死锁. (synchronized 是可重入锁, 没有这个问题)

场景2: 两个线程两把锁

比如: 家里钥匙锁车里了, 车钥匙锁家里了.

public class Test {
    public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();

        Thread t1 = new Thread(() -> {
            System.out.println("t1 尝试获取 locker1");
            synchronized (locker1) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("t1 尝试获取 locker2");
                synchronized (locker2) {
                    System.out.println("t1 获取两把锁成功!");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            System.out.println("t2 尝试获取 locker2");
            synchronized (locker2) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2 尝试获取 locker1");
                synchronized (locker1) {
                    System.out.println("t2 获取两把锁成功!");
                }
            }
        });

        t1.start();
        t2.start();
    }
}

运行结果展示, 无法再获取到锁.

场景3: 多个线程多把锁 (更容易死锁)

哲学家就餐问题

有五个哲学家, 五根筷子. 五根筷子分别放到每两个哲学家之间.

每个哲学家只会做两件事:

  1. 思考, 什么都不做(线程阻塞了)
  2. 吃面条, 先拿起左手的筷子, 再拿起右手的筷子, 吃面条, 吃完放下筷子.

大部分情况, 这个模型是可以良好运行的, 不会发生死锁.

但如果五个哲学家, 在同一时间, 同时拿起左手的筷子. 再拿右手的筷子时, 发现右手边没有筷子. 并且这几个哲学家互不相让, 此时就会陷入僵局.


2. 如何避免死锁

死锁产生的四个必要条件:

  1. 互斥使用. 锁A 被线程1 占用, 线程2 就不用了.
  2. 不可抢占. 锁A 被线程1 占用, 线程2 不能把锁A 给抢过来, 除非线程1 主动释放.
  3. 请求和保持. 有多把锁, 线程1 拿到锁A 之后, 不想释放锁A , 还想拿到一个锁B.
  4. 循环等待. 线程1 等待线程2 释放锁, 线程2 释放锁等待线程1 释放锁.

所以我们只要打破以上四种的其中一个, 就可以避免死锁

  1. 互斥使用, 不可打破, 是锁的基本特性
  2. 不可抢占, 不可打破, 是锁的基本特性
  3. 请求和保持, 有可能打破, 取决于代码的写法, 看获取锁B 的时候是不是先释放锁A. (主要看需求场景是否允许这么写)
  4. 循环等待, 有把握打破! 只要约定好加锁的顺序, 就可以打破循环等待.

比如: 给锁编号, 约定, 加多个锁的时候, 必须先加编号小的锁, 后加编号大的锁. 就可以有效避免循环等待.


哲学家就餐问题

我们给筷子进行编号, 先拿编号小的, 后拿编号大的. (不再是先拿左手再拿右手)

假设这时五个哲学家又同时伸手拿第一支筷子(尝试获取第一把锁)

继续向下, 直到👸吃完, 👼开始吃面.

还有一个解决方案, 为 “银行家算法”, 就是把所有的资源统一进行统筹分配, 也能避免死锁. 但这个方法最大的问题就是比较复杂, 不太适合实际开发.


🌷(( ◞•̀д•́)◞⚔◟(•̀д•́◟ ))🌷

以上就是今天要讲的内容了,希望对大家有所帮助,如果有问题欢迎评论指出,会积极改正!!
在这里插入图片描述
加粗样式

这里是Gujiu吖!!感谢你看到这里🌬
祝今天的你也
开心满怀,笑容常在。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值