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


    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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

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

    场景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吖!!感谢你看到这里🌬
    祝今天的你也
    开心满怀,笑容常在。
  • 相关阅读:
    基于JSP实现的作业管理系统
    Vue实现多角色登录,Vue-Router路由守卫控制权限页面
    使用阿里云服务器学习Docker
    数字化转型领航者:佑美科技塑造智能健康新生态
    RustDay06------Exercise[91-100]
    多数据源Pagehelper怎么配置
    c# 多线程创建及线程同步
    Dcoker入门,小白也学得懂!
    camera的sensor相关知识笔记
    【FDTD 反射、透射、吸收 软件操作】
  • 原文地址:https://blog.csdn.net/m0_58592142/article/details/127361098