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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 职业炒股怎么操作_专业炒股的人天天都在干嘛

    职业炒股怎么操作_专业炒股的人天天都在干嘛转自http://blog.sina.com.cn/s/blog_4af4b1c20100f47u.html 一、我买哪只股票好?买你最熟悉的上市公司,同城的最好。记住,买卖时机大于选股。二、我持有的某某股票现在卖好吗?只要有赢利,任何时候抛都是好点。三、我准备借钱来炒股,好吗?借钱就等于是资金杠杆,你得有必胜的把握!这世界上利用杠杆在资本市场获大利的数不胜数。四、我准备专职炒股,好吗?以…

    2025年6月30日
    3
  • Java中Scanner 的用法/ Scanner怎么使用

    Java中Scanner 的用法/ Scanner怎么使用Java中要想输入时便要用到Scanner首先在使用之前导入util包要想通过控制台进行输入,首先要构造一个Scanner对象,它附属于”标准输入流Scannerin=newScanner(System.in);现在我们就可以使用Scanner类的各种方法了使用Scanner读取字符串/整数/浮点数importjava.util.Scanner;//需要导入util包S…

    2022年7月20日
    16
  • springboot整合mybatis实现增删改查

    springboot整合mybatis实现增删改查立志存高远,笃行践初心三更灯火五更鸡,正是男儿读书时。黑发不知勤学早,白首方悔读书迟。立志,标定人生方向;奋斗,创造人生价值,二者相辅相成,互相促进。大部分程序员的「目标」都是成为一名优秀的工程师,一名可以统览全局的「架构师」。千里之行始于足下对于大部分普通人而言,成为一名优秀的架构师还是有一定难度的,「千里之行始于足下,一步一个脚印,慢慢来」。????粉丝专属福利:包邮送书3本,如下书单四选一。????获取方式:1、参与文末投票,点赞,收藏即有机会获得精

    2022年6月15日
    30
  • python stdout

    python stdoutpythonstdout

    2025年5月26日
    2
  • PHP常用的开发工具_小程序开发工具有哪些

    PHP常用的开发工具_小程序开发工具有哪些互联网的流行使得,软件程序发的需求也越来越大,其中PHP程序开发就是一个先例。PHP是英文超级文本预处理语言HypertextPreprocessor的缩写。PHP是一种HTML内嵌式的语言,

    2022年8月3日
    6
  • vmware15激活码【2021.8最新】

    (vmware15激活码)这是一篇idea技术相关文章,由全栈君为大家提供,主要知识点是关于2021JetBrains全家桶永久激活码的内容IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.html70YZDJVTFP-eyJsa…

    2022年3月26日
    112

发表回复

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

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