7-线程死锁[通俗易懂]

7-线程死锁[通俗易懂]线程死锁什么是线程死锁?死锁指的是两个或两个以上的线程在执行过程中因为争夺资源而造成的互相等待的现象。在无外力的情况下,这些线程一直会相互等待而无法继续进行工作。如图:在上图中,线程A已经持有了资

大家好,又见面了,我是你们的朋友全栈君。

线程死锁

  • 什么是线程死锁?死锁指的是两个或两个以上的线程在执行过程中因为争夺资源而造成的互相等待的现象。在无外力的情况下,这些线程一直会相互等待而无法继续进行工作。如图:

  • image

  • 在上图中,线程A已经持有了资源2,它同时还想申请资源1,线程B已经持有了资源1,它同时还想申请资源2,所以线程A和线程B就因为相互等待对方已经持有的资源,而进入死锁状态。

  • 为什么产生死锁呢?死锁产生的必备条件:

    • 互斥条件:指线程对已经获得到的资源进行排它使用,如果还有其他线程想用这个资源,则必须等待。
    • 请求并持有条件:指一个线程已经持有了至少一个资源,但又提出了新的资源请求,而新的资源请求已经被其他线程锁占有且为互斥。
    • 不可剥夺条件:指线程获取到的资源在自己使用完之前不能被其他线程所抢占,只有在自己使用完后才能由自己释放。
    • 环路等待条件:指发生死锁的时候必然存在一个线程-资源的环形链,即线程集合{T0,T1,T2,…,Tn}中的T0等待T1占用的资源,T1等待T2占用的一个资源,以此类推,Tn等待T0占用的一个资源。
    package com.heiye.learn1;
    
    import java.util.logging.Logger;
    
    public class DeadLockTest2 {
        //创建资源
        private static Object resourceA = new Object();
        private static Object resourceB = new Object();
    
        public static void main(String[] args) {
            Logger logger = Logger.getLogger(DeadLockTest2.class.toString());
            //创建线程A
            Thread threadA = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (resourceA) {
                        logger.info(Thread.currentThread() + " get ResourceA");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        logger.info(Thread.currentThread() + " waiting get ResourceB");
                        synchronized (resourceB) {
                            logger.info(Thread.currentThread() + "get ResourceB");
                        }
                    }
                }
            });
    
            //创建线程B
            Thread threadB = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (resourceB) {
                        logger.info(Thread.currentThread() + " get ResourceB");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        logger.info(Thread.currentThread() + " waiting get ResourceA");
                        synchronized (resourceA) {
                            logger.info(Thread.currentThread() + "get ResourceA");
                        }
                    }
                }
            });
    
            //启动线程
            threadA.start();
            threadB.start();
        }
    }
    
  • image

  • Thread-2是线程B,Thread-1是线程A,从输出结果可知,线程调度器首先调度了线程B,也就是吧CPU时间片资源分配给了B,线程B通过synchronized(ResourceB)获取到了B资源锁,然后休眠1s是为了保证线程B为了获取资源A之前先让线程A获取资源A,这样线程A获取到了资源A,线程B获取到了资源B,但是线程暗想获取资源B的资源,线B又想获取资源A的资源,这样就互相等待想入了死锁。

  • 那么如何避免线程死锁呢?只需要破坏掉至少一个构造死锁的条件就可以了。造成死锁的原因其实和申请自愿的顺序有着很大的关系,使用资源申请的有序性原则就可以避免死锁,那么什么是资源申请的有序性?我们对线程B的代码进行修改

            //创建线程B
            Thread threadB = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (resourceA) {
                        logger.info(Thread.currentThread() + " get ResourceB");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        logger.info(Thread.currentThread() + " waiting get ResourceA");
                        synchronized (resourceB) {
                            logger.info(Thread.currentThread() + "get ResourceA");
                        }
                    }
                }
            });
    
  • image

  • 在上面的代码中,假如线程A和线程B同时执行了synchronized(resourceA),只有一个线程可以获取到resourceA上的监视器,假如线程A获取到了,那么线程B就会阻塞而不会再去获取资源B,线程A获取到resourceA的监视器后回去申请resourceB资源,这时候线程A是获取到的,线程A获取到resourceB并使用完之后会放弃对资源resourceB的持有,然后再释放resourceA的持有,释放resourceA后线程B才会被从阻塞阶段变为激活状态。所以资源的有序性破坏了资源的请求并持有条件和环路等待条件,因此避免了死锁。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/154418.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • linux直接运行py文件_linux的系统调用

    linux直接运行py文件_linux的系统调用python下编译py成pyc和pyo(文件加密)需要注意的是,编译成pyc或者pyo文件后需要将命名改成与源Python命名一致,将其放在源目录下,虽然其他python文件调用pyd时显示不能检测到该模块,但实际上可以运行。由于pyc的编译收到python版本的影响,所以当将编译后的pyc迁移到另一台电脑中时,最好保持python环境一致。将python文件.py编译成pyc二进制文件:pyt…

    2022年10月24日
  • Python如何生成exe文件?用Pycharm一步一步带着你学(超详细、超贴心)

    Python如何生成exe文件?用Pycharm一步一步带着你学(超详细、超贴心)目的描述:为了让没有安装Python的人也能使用我们编写的.py文件,我们需要将编写好的Python程序生成.exe文件。第一步下载pyinstallerpyinstaller插件是Python自带的插件,用于为我们写好的代码进行打包,最终自动合成.exe文件。在Pycharm界面的最下面,你可以看到Terminal,选择这个选项,这就是一个终端界面。在此界面写输入指令:pipin…

  • java webservice接口开发教程_JAVA入门教程

    java webservice接口开发教程_JAVA入门教程写在前面的话:当两个人碰面后,产生了好感,如果需要得到双方的信息,那么双方的交流是必不可少的!应用程序也如此,各个应用程序之间的交流就需要WebService来作为相互交流的桥梁!项目目的:程序A调用程序B中的方法C…首先申明:本次需要采用的JDK版本为jdk1.6+,也就是要java6+才能看到效果,java5没试过…我认为,升级一下JDK版本比起安装什么Axis以及导入一堆jar包和配…

  • Sphinx + Coreseek 实现中文分词搜索

    Sphinx + Coreseek 实现中文分词搜索

  • 如何从零开始学android?

    如何从零开始学android?不知不觉进入软件开发这一行业已经8年了,回想起刚毕业在深圳一个月拿着2000的工资,还要在休息时间自学android,感觉那段时光真的是大学毕业后最充实的一段时光了;努力总会有结果的,第二年涨到5000,第三年跳槽回郑州给了我7500,主要是离家近,父母年纪大了,给的钱少点无所谓,起码能经常回家看看。回忆到此结束,之前看到有人在悟空问答提问怎么从零开始学android这个问题,但是限于爪机无力…

  • Web服务器配置(服务器配置信息怎么查)

    课程名称服务器配置与管理实验成绩 实验名称Web服务器配置学号 姓名;指导老师-龚蕾 班级 日期 实验目的:1.掌握liunx系统的基本命令2 掌握Web服务器配置的基本原理3.掌握Apache服务器的安装与配制方法和客户端的测试方法实验平台:  云平台一、  实验内容配置Web服务器,可以从客户端访问服务器,打开网页。二、  服务器端设置1、    配置DNS服务器,要求能够解析域名。2、   …

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号