Java 进阶—死锁造成原因及其解决

今天我们来了解一下线程死锁,死锁很好理解,从字面上来看就是锁死了,解不开,在大街上看到一对卧龙凤雏的情侣,怎么说,你们给我锁死,不要分开去霍霍别人

之前我们不是说过,解决线程安全的方法就是给线程上锁,java进阶—线程安全问题,但上锁也会有死锁的情况

那么,死锁是什么?先举个通俗点的例子 小明跟小红分别同时参加两个会议,这时候办公室刚好只有一台笔记本(在小红手上),一台投影仪(在小明手上),这是两个都想要对方的东西,两人互不相让,开始争执,这样都开不成会议,就形成了死锁

在这里插入图片描述
把小明跟小红换成两个线程,所以,一句话,死锁就是两个或两个以上的线程争夺彼此的锁,造成阻塞

从这里我们也可以看到死锁产生的条件

  • 首先,死锁产生需要两个或者两个以上线程 (例子中的小明跟小红)

  • 两个或者两个以上的锁 (例子中的 笔记本跟投影仪)

  • 两个或两个以上线程持有不同锁(例子中小明有投影仪,小红有笔记本)

  • 持有不同锁线程争夺对方的锁 (例子中小明跟小红抢对方的东西)

用代码来解释上述例子

我们按照死锁的四个条件一步一步来

1. 首先有两个线程

一个小明类,一个小红类

2. 两个锁

小明类 里面 有 投影仪锁 , 小红类里面有电脑锁

3. 两个线程持有不同的锁

小明重写run 方法 调用 投影仪 锁

小红重写run 方法 调用 笔记本 锁

4. 持有不同锁调用对方的锁

小明在projector 里面去调用 小红的computer

小红在computer里面去调用 小红的projector


public class XiaoMing  implements Runnable{


    @Override
    public void run() {
        //小明持有投影仪这个锁
        projector();
    }

    /**
     *  投影仪这个锁  static 一个资源
     */
    public static synchronized  void projector() {
        try {
            //线程休眠2秒,目的为了执行慢一点,更方便看出效果
            Thread.sleep(2000);
        } catch (InterruptedException e) { 
            e.printStackTrace();
        }
        System.out.println("小明的投影仪"); 
        // 获取小红的锁
        XiaoHong.computer();
    }
}
public class XiaoHong  implements Runnable{

    @Override
    public void run() { 
        //小红持有笔记本这个锁
        computer();
    }

    /**
     * 笔记本这个锁   static 一个资源
     */
    public static synchronized  void computer() {
        try {
            //线程休眠2秒
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("小红的笔记本");
        //获取小明的锁
        XiaoMing.projector();
    }
}

我们去启动这两个线程

    public static void main(String[] args) {
        //创建小红线程
        XiaoHong xiaoHong =new XiaoHong();
        Thread thread =new Thread(xiaoHong);
        //创建小明线程
        XiaoMing xiaoMing = new XiaoMing();
        Thread thread1 =new Thread(xiaoMing);
        //启动
        thread.start();
        thread1.start();
    }

执行结果:

在这里插入图片描述
程序在打印两个结果后,两个程序在相互争夺资源,程序停止需要借助外力

解决死锁也就很简单了,我们不去拿对方的锁就好了

在这里插入图片描述
这一步,我们不去调用对方的锁,

看看效果:
在这里插入图片描述
可以看到程序正常终止了

要是不太明白,我们再来看一个更直观的代码:(注意看代码中的注释)

还是有这么两个资源,电脑跟投影仪


public class Person implements Runnable {
    /**
     * 只有一份资源,电脑  跟 投影仪
     */
    static final Computer COMPUTER = new Computer();
    static final Projector PROJECTOR = new Projector();

    /**
     * 类型 0 代表 小红  1代表小明
     */
    int  type;
    /**
     * 人名
     */
    String  name;

    public Person(int type, String name) {
        this.type = type;
        this.name = name;
    }

    @Override
    public void run() {
       //开始抢占资源
        try {
            getSource();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void getSource() throws InterruptedException {
        //让小明先拿投影仪
        if (type==1) {
            synchronized (PROJECTOR) {
                System.out.println(this.name+"获得投影仪");
                Thread.sleep(2000);
                //小明拿到投影仪,又想去拿电脑
                synchronized (COMPUTER) {
                    System.out.println(this.name+"获得电脑");
                }
            }

        }else {
            //小红拿到电脑,又想去拿投影仪
            synchronized (COMPUTER) {
                System.out.println(this.name+"获得电脑");
                Thread.sleep(1000);

                synchronized (PROJECTOR) {
                    System.out.println(this.name+"获得投影仪");
                }
            }

        }
    }

}

启动两个线程

  public static void main(String[] args) {

        Person person =new Person(1,"小明");
        Person person2 =new Person(0,"小红");
        Thread thread =new Thread(person);
        Thread thread1 =new Thread(person2);
        thread.start();
        thread1.start();
    }

执行结果:
在这里插入图片描述
程序进入了死锁

怎么解决?一样的,获取到锁的同时不去调用对方的锁,我们这两段代码放到外边来

在这里插入图片描述
变成下面这样

在这里插入图片描述

package com.xrp.flinkDemo.demo;

public class Person implements Runnable {
    /**
     * 只有一份资源,电脑  跟 投影仪
     */
    static final Computer COMPUTER = new Computer();
    static final Projector PROJECTOR = new Projector();

    /**
     * 类型 0 代表 小红  1代表小明
     */
    int  type;
    /**
     * 人名
     */
    String  name;

    public Person(int type, String name) {
        this.type = type;
        this.name = name;
    }

    @Override
    public void run() {
       //开始抢占资源
        try {
            getSource();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void getSource() throws InterruptedException {
        //让小明先拿投影仪
        if (type==1) {
            synchronized (PROJECTOR) {
                System.out.println(this.name+"获得投影仪");
                Thread.sleep(2000);
            }

            synchronized (COMPUTER) {
                System.out.println(this.name+"获得电脑");
            }

        }else {
            //小红拿到电脑,又想去拿投影仪
            synchronized (COMPUTER) {
                System.out.println(this.name+"获得电脑");
                Thread.sleep(1000);
            }


            synchronized (PROJECTOR) {
                System.out.println(this.name+"获得投影仪");
            }

        }
    }

}

再执行看看:
在这里插入图片描述
可以发现解决了死锁问题,并且都获得了想要的资源

好了,以上便是死锁原因以及避免死锁方法了,主要就是不要在持有一个锁里面再去获取别的锁

【完】

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值