死锁的原因及解决方法

死锁的原因及解决方法

1.什么是死锁

死锁是在多个线程或进程之间,彼此持有对方所需的资源,并且同时需要等待对方释放,从而导致参与者都无法继续执行的状态.

举个简单的例子:假设有两个线程t1和t2,他们分别需要获取X资源和Y资源才能只执行,如果线程T1先获取了资源X,而线程T2先获取了资源Y,他们分别等待对方释放所需要的资源,那么他们将陷入死锁的状态,无法继续执行.

2.死锁的三大经典情况

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

java 复制代码
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是可重入锁

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

java 复制代码
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();
    }
}

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

假设有5位哲学家围坐在一个圆形餐桌前,每个哲学家面前有一个碗和一只筷子,哲学家们思考(程序中表示运行),但只有同时拿到左右两只筷子才能吃饭.

如果哲学家发现筷子拿不起来(被别人占用了),就会陷入阻塞等待

如果五个哲学家同时拿起左手边的筷子,再尝试拿右手边的筷子,就会发现右手的筷子都被占用,这个时候就形成了死锁.

3.死锁产生的原因

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

4.如何避免死锁

按固定的顺序获取锁:确保所有线程都按照相同的顺序获取锁,这样就可以避免不同线程之间因获取锁的顺序不同而导致死锁的发生.

确保所有线程按照相同的顺序获取锁:1.确定一个固定的获取锁的顺序,比如locker1在locker2之前获取到锁2.所有线程必须在按照这个约定来获取资源.

java 复制代码
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();
    }
}
相关推荐
如竟没有火炬3 分钟前
全排列——交换的思想
开发语言·数据结构·python·算法·leetcode·深度优先
浓墨染彩霞7 分钟前
Java----set
java·经验分享·笔记
5pace7 分钟前
【JavaWeb|第一篇】Maven篇
java·maven
嵌入式小李.man16 分钟前
C++第十三篇:继承
开发语言·c++
Bryce李小白22 分钟前
Kotlin Flow 的使用
android·开发语言·kotlin
jarreyer1 小时前
python离线包安装方法总结
开发语言·python
李辰洋1 小时前
go tools安装
开发语言·后端·golang
Code_Geo1 小时前
agent设计模式:第三章节—并行化
java·设计模式·agent·并行化
Javatutouhouduan1 小时前
2025Java高质量面试真题汇总!
java·高并发·java面试·java面试题·后端开发·java架构师·java八股文
wanfeng_091 小时前
go lang
开发语言·后端·golang