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才会被从阻塞阶段变为激活状态。所以资源的有序性破坏了资源的请求并持有条件和环路等待条件,因此避免了死锁。

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • neokylin 系统_centos kde

    neokylin 系统_centos kdeNeoKylin(linux)操作系统基本操作使用的虚拟机工具是VMware虚拟机远程控制工具MobaXterm操作系统:NeoKylin操作系统,(GUI,桥接模式)NeoKylin相关操作开关机操作:init,shutdowm-t/-k/-r/-h…切换用户:suXXX查询文件/目录:ls,ll进入目录:cd绝对路径,相对路径设置别名:alias/unalias查看帮助:help/-hNeoKylin文件操作创建文件touch,vim,echo创建目录m

    2022年8月10日
    7
  • springsecurity自定义密码验证_数字图形验证码怎么填

    springsecurity自定义密码验证_数字图形验证码怎么填SpringSecurity添加图形验证码认证功能第一步:图形验证码接口1.使用第三方的验证码生成工具Kaptchahttps://github.com/penggle/kaptcha@ConfigurationpublicclassKaptchaImageCodeConfig{@BeanpublicDefaultKaptchagetDefaultKaptcha(){DefaultKaptchadefaultKaptcha=newDefa

    2022年9月15日
    3
  • 长尾分布和重尾分布「建议收藏」

    长尾分布和重尾分布「建议收藏」文章来源:长尾分布,重尾分布(Heavy-tailedDistribution)-Shiyu_Huang-博客园https://www.cnblogs.com/huangshiyu13/p/6217180.html长尾分布,重尾分布(Heavy-tailedDistribution)Zipf分布:Zipf分布是一种符合长尾的分布:  就是指尾巴很长的分布。那么尾巴很长很厚的分布有什么特…

    2025年5月29日
    1
  • 免费空间(免备案,无广告) 1G免费全能空间

    免费空间(免备案,无广告) 1G免费全能空间云邦互联免费空间(免备案,无广告)【1G免费全能空间,免备案,无广告】 1G全能空间+100M数据库(Mysql5.5/SQLServer2005) 支持的脚本:ASP、PHP(5.2-7.0)、.NET(2.0/4.0) 没有任何限制,详细功能请访问: https://www.yunzz.net/host/free.html(云邦互联) 推广

    2022年6月28日
    45
  • python进阶(11)生成器[通俗易懂]

    python进阶(11)生成器[通俗易懂]生成器利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。

    2022年7月29日
    7
  • 《Android Studio开发实战 从零基础到App上线(第2版)》资源下载和内容勘误[通俗易懂]

    《Android Studio开发实战 从零基础到App上线(第2版)》资源下载和内容勘误[通俗易懂]资源下载下面是《AndroidStudio开发实战从零基础到App上线(第2版)》一书用到的工具和代码资源:1、本书使用的AndroidStudio版本为3.2,最新的安装包可前往Android官网页面下载。2、本书使用的AndroidNDK版本为r17,最新的安装包可前往Android官网页面下载。3、本书提供所有示例源码的demo工程下载,源码(适配Android4.1到9….

    2022年5月30日
    37

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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