• 如何理解死锁?


    目录

    🐳今日良言:等风来,不如追风去

    🐕一、死锁

    🐇1.概念

    🐇2.死锁的三个典型情况

    🐇3.死锁的必要条件

    🐇4.如何破除死锁


    🐳今日良言:等风来,不如追风去

    🐕一、死锁

    🐇1.概念

    多个线程在争夺资源时,陷入了僵持状态,都无法进行下去,都在等待对方释放资源,如果不干涉,将一直僵持下去

    🐇2.死锁的三个典型情况

    通过这三个典型情况,可以帮助理解死锁

    1).一个线程一把锁,连续加锁两次,如果锁是不可重入锁就会死锁

    2).两个线程两把锁,t1和t2先分别针对锁A和锁B进行加锁,再尝试获取对方的锁.

        t1先拿到锁A,然后获取锁B,t2先拿到锁B,然后获取锁A,此时就会发生死锁.

        举个例子:有一天小马和女朋友出去吃饺子,两人都喜欢用酱油和醋蘸着饺子吃,饺子上好以

        后,小马拿起了醋,女朋友拿起了酱油,然后女朋友对小马说:你把醋给我,我用完了就给你. 然

        后,小马说:你把酱油给我,我用完了给你.如果此时两个人互不相让,此时就发生死锁.

        针对这种情况,通过下面代码来加深理解:

    1. public class ThreadDemo21 {
    2. public static void main(String[] args) {
    3. // 创建两个实例:jiangyou和cu
    4. Object jiangyou = new Object();
    5. Object cu = new Object();
    6. // 创建两个线程
    7. Thread t1 = new Thread(() -> {
    8. // 先拿到cu
    9. synchronized (cu) {
    10. try {
    11. // 为了让t2拿到jiangyou
    12. Thread.sleep(1000);
    13. } catch (InterruptedException e) {
    14. e.printStackTrace();
    15. }
    16. synchronized (jiangyou) {
    17. System.out.println("小马拿到了醋和酱油");
    18. }
    19. }
    20. });
    21. Thread t2 = new Thread(() -> {
    22. // 先拿到cu
    23. synchronized (jiangyou) {
    24. try {
    25. Thread.sleep(1000);
    26. } catch (InterruptedException e) {
    27. e.printStackTrace();
    28. }
    29. synchronized (cu) {
    30. System.out.println("女朋友拿到了醋和酱油");
    31. }
    32. }
    33. });
    34. t1.start();
    35. t2.start();
    36. }
    37. }

    此时运行代码,会发现程序无任何运行结果,说明没有线程拿到两把锁.

    使用jconsole观察当前线程的情况

     如果打开jconsole没有进程的话,以管理员身份运行

    3).多个线程多把锁

    经典案例:哲学家就餐问题

    问题描述:假设现在餐桌上有五个哲学家,每个哲学家的面前都放着一碗面条,桌子上有5根筷子,每个哲学家都需要两根筷子才可以吃面条,每个哲学家都有两种状态:1.思考人生(相当于线程的阻塞状态)   2.拿起筷子吃面条(相当于线程获取到锁,执行指令)    由于操作系统的随机调度,这5个哲学家随时都可能想吃面条或者思考人生,要想吃面条必须拿起左右手的两根筷子,情况图如下:

    假设在某一时刻,发生极端情况,此时就会发生死锁

    例如:同一时刻,所有哲学家都拿起左手的筷子,此时哲学家们都拿不到右手的筷子,都需要等待右边的哲学家将筷子放下.

    🐇3.死锁的必要条件

    死锁有四个必要条件:

    1).互斥使用

        线程1拿到了锁,线程2就只能阻塞等待,无法使用锁.

    2).不可抢占

        线程1拿到锁后,线程2不能强夺锁,只能阻塞等待,等待线程1释放锁

    3).请求和保持

        线程1拿到锁A后,再尝试获取锁B,此时锁A还是保持的(并不会因为要获取锁B就释放锁A)

    4).循环等待

        线程1尝试获取锁A和锁B   线程2尝试获取锁B和锁A

        线程1在获取锁B的时候等待线程2释放锁B,线程2在获取锁A的时候等待线程1释放锁A

    🐇4.如何破除死锁

    在死锁的四个必要条件中,前三个都是锁的基本特性,我们无法更改,所以说破除死锁的突破口就是:循环等待

    如何破除循环等待呢?

    最简单的办法:给锁编号,然后指定一个固定的顺序(比如从小到大)来加锁,任意线程加多把锁的时候,都让线程遵守上述顺序,此时循环等待自然破除.

    以哲学家就餐问题为例:破除循环等待,为每一个筷子编号,从小到大,然后从第一个哲学家开始,规定:每次先拿编号小的筷子,然后拿编号大的筷子.

    此时,对于哲学家5来说,无法拿起筷子,因为编号1的筷子已经被哲学家1拿走了,此时哲学家5阻塞等待,此时,对于哲学家4来说,他就可以拿起编号为5的筷子,然后吃面条,吃完后,放下筷子,此时哲学家3可以吃面条,以此类推,最后死锁就会被破除了.

    针对上面的两个线程两把锁的代码,破除循环等待的代码如下.

    1. public class ThreadDemo21 {
    2. public static void main(String[] args) {
    3. // 规定:jiangyou 是1号 cu 是2号 约定:先拿小的,再拿大的
    4. Object jiangyou = new Object();
    5. Object cu = new Object();
    6. // 创建两个线程
    7. Thread t1 = new Thread(() -> {
    8. // 先拿到cu
    9. synchronized (jiangyou) {
    10. try {
    11. // 为了让t2拿到jiangyou
    12. Thread.sleep(1000);
    13. } catch (InterruptedException e) {
    14. e.printStackTrace();
    15. }
    16. synchronized (cu) {
    17. System.out.println("小马拿到了醋和酱油");
    18. }
    19. }
    20. });
    21. Thread t2 = new Thread(() -> {
    22. // 先拿到cu
    23. synchronized (jiangyou) {
    24. try {
    25. Thread.sleep(1000);
    26. } catch (InterruptedException e) {
    27. e.printStackTrace();
    28. }
    29. synchronized (cu) {
    30. System.out.println("女朋友拿到了醋和酱油");
    31. }
    32. }
    33. });
    34. t1.start();
    35. t2.start();
    36. }
    37. }

     此时运行代码:

     死锁的问题就得到了解决.

    还有一种办法破除死锁的办法,银行家算法,但是过于复杂,这里只介绍简单的解决死锁的办法

  • 相关阅读:
    汽车屏类产品(三):抬头显示Head-Up Display(HUD)
    判断一个Java服务是不是GateWay
    顾曼宁(顾曼):漂流伞创始人与杭州高层次人才的杰出代表
    设计模式-享元模式
    如何减少软件设计和实现之间鸿沟
    【FreeRtos任务通知详解】
    安装前期开发的环境
    Flume在大数据集群下的配置以及监控工具Ganglia的部署安装
    电子制造企业部署WMS仓储管理系统的好处是什么
    博客主题美化第二弹
  • 原文地址:https://blog.csdn.net/qq_54469537/article/details/128197212