• 死锁的原因及解决方法


    1.什么是死锁

    死锁是在多个线程或进程之间,彼此持有对方所需的资源,并且同时需要等待对方释放,从而导致参与者都无法继续执行的状态.
    举个简单的例子:假设有两个线程t1和t2,他们分别需要获取X资源和Y资源才能只执行,如果线程T1先获取了资源X,而线程T2先获取了资源Y,他们分别等待对方释放所需要的资源,那么他们将陷入死锁的状态,无法继续执行.

    2.死锁的三大经典情况

    1.一个线程,一把锁,但是是不可重入锁,该线程针对这个锁连续加锁两次,就会出现死锁

    public class Demo {
        private static Object lock = new Object();
    
        public static void main(String[] args) {
            Thread t1 = new Thread(()-> {
               synchronized (lock) {
                   System.out.println("线程持有锁");
               }
               synchronized (lock) {
                   System.out.println("不会执行到");
               }
            });
            t1.start();
        }
    }
    //线程t1先获取了锁lock,在持有锁的状态下再次尝试获取了相同的锁
    //由于是不可重入锁导致第二次获取锁时被阻塞,无法执行,就出现了死锁,
    //注意:synchronized是可重入锁
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.两个线程,两把锁,两个线程分别获取到一把锁,然后再同时获取对方的锁

    public class Demo {
        private static Object X = new Object();
        private static Object Y = new Object();
        public static void main(String[] args){
            Thread t1 = new Thread(()->{
                synchronized (X) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    synchronized (Y) {
                        System.out.println("t1两把锁加锁成功");
                    }
                }
            });
            Thread t2 = new Thread(()->{
                synchronized (Y) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    synchronized (X) {
                        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

    3.N个线程M把锁:哲学家就餐问题

    假设有5位哲学家围坐在一个圆形餐桌前,每个哲学家面前有一个碗和一只筷子,哲学家们思考(程序中表示运行),但只有同时拿到左右两只筷子才能吃饭.
    在这里插入图片描述
    如果哲学家发现筷子拿不起来(被别人占用了),就会陷入阻塞等待
    如果五个哲学家同时拿起左手边的筷子,再尝试拿右手边的筷子,就会发现右手的筷子都被占用,这个时候就形成了死锁.

    3.死锁产生的原因

    死锁的必要四个条件:
    1.互斥条件:一个资源只能被一个线程使用,如果一个线程已经获取了某个资源,其他线程就无法再获取该资源,只能等待.锁的基本特性
    2.不可抢占:资源不能被强制性的从一个线程中抢占,只能由持有它的线程自愿释放.锁的基本特性
    3.请求和保持:当资源请求者在请求其他的资源的同时还保持对原有资源的占有取决代码结构
    4.循环等待:存在一个循环循环链,t1尝试获取Y,需要t2执行完,释放Y;t2尝试获取X,需要t1执行完,释放X取决代码结构

    4.如何避免死锁

    按固定的顺序获取锁:确保所有线程都按照相同的顺序获取锁,这样就可以避免不同线程之间因获取锁的顺序不同而导致死锁的发生.
    确保所有线程按照相同的顺序获取锁:1.确定一个固定的获取锁的顺序,比如locker1在locker2之前获取到锁2.所有线程必须在按照这个约定来获取资源.

    public class Demo4 {
        private static Object locker1 = new Object();
        private static Object locker2 = new Object();
        public static void main(String[] args) {
            Thread t1 = new Thread(() ->{
               synchronized (locker1) {
                   System.out.println("持有locker1");
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       System.out.println("等待locker2");
                   }
                   synchronized (locker2) {
                       System.out.println("获取到locker2");
                   }
               }
            });
            Thread t2 = new Thread(()->{
               synchronized (locker1) {
                   System.out.println("持有locker1");
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       System.out.println("等待locker2");
                   }
                   synchronized (locker2) {
                       System.out.println("获取到locker2");
                   }
               }
            });
            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
  • 相关阅读:
    Linux安全--iptables详解
    常识java-错误: 编码 GBK 的不可映射字符 (0x80)
    【Android性能】【流畅度】概念初识
    19.删除链表的倒数第 N 个节点
    Ubuntu20.04 lts设置开机不进图形界面直接启动程序教程
    地下城规划3d全景vr虚拟现实制作提高沟通效率
    platform驱动框架梳理
    Codeforces Round #816 (Div. 2)
    YOLO目标检测——跌倒摔倒数据集【含对应voc、coco和yolo三种格式标签】
    JLPT N2 文法 Day 1
  • 原文地址:https://blog.csdn.net/qq_64552046/article/details/134175508