死锁的原因及解决方法

死锁的原因及解决方法

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();
    }
}
相关推荐
冷雨夜中漫步8 小时前
Python快速入门(6)——for/if/while语句
开发语言·经验分享·笔记·python
JH30739 小时前
SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
java·spring boot·spring
m0_7369191010 小时前
C++代码风格检查工具
开发语言·c++·算法
Coder_Boy_10 小时前
技术让开发更轻松的底层矛盾
java·大数据·数据库·人工智能·深度学习
2501_9449347310 小时前
高职大数据技术专业,CDA和Python认证优先考哪个?
大数据·开发语言·python
invicinble10 小时前
对tomcat的提供的功能与底层拓扑结构与实现机制的理解
java·tomcat
较真的菜鸟10 小时前
使用ASM和agent监控属性变化
java
黎雁·泠崖10 小时前
【魔法森林冒险】5/14 Allen类(三):任务进度与状态管理
java·开发语言
2301_7634724611 小时前
C++20概念(Concepts)入门指南
开发语言·c++·算法
TechWJ12 小时前
PyPTO编程范式深度解读:让NPU开发像写Python一样简单
开发语言·python·cann·pypto