死锁的原因及解决方法

死锁的原因及解决方法

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 小时前
【MATLAB】MATLAB R2025a 详细下载安装图文指南:下一代科学计算与工程仿真平台
开发语言·matlab·开发工具·工程仿真·matlab r2025a·matlab r2025·科学计算与工程仿真
2601_949146534 小时前
C语言语音通知API示例代码:基于标准C的语音接口开发与底层调用实践
c语言·开发语言
开源技术4 小时前
Python Pillow 优化,打开和保存速度最快提高14倍
开发语言·python·pillow
学嵌入式的小杨同学4 小时前
从零打造 Linux 终端 MP3 播放器!用 C 语言实现音乐自由
linux·c语言·开发语言·前端·vscode·ci/cd·vim
毕设源码-朱学姐4 小时前
【开题答辩全过程】以 基于JavaWeb的网上家具商城设计与实现为例,包含答辩的问题和答案
java
mftang5 小时前
Python 字符串拼接成字节详解
开发语言·python
jasligea6 小时前
构建个人智能助手
开发语言·python·自然语言处理
kokunka6 小时前
【源码+注释】纯C++小游戏开发之射击小球游戏
开发语言·c++·游戏
C雨后彩虹6 小时前
CAS与其他并发方案的对比及面试常见问题
java·面试·cas·同步·异步·
云栖梦泽7 小时前
易语言开发从入门到精通:补充篇·网络编程进阶+实用爬虫开发·API集成·代理IP配置·异步请求·防封禁优化
开发语言