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)
上一篇 2022年7月4日 下午1:00
下一篇 2022年7月4日 下午1:16


相关推荐

  • S3C2440 之SPI

    S3C2440 之SPI概述:S3C2440有两个串行外设SPI接口,SPI具有全双工通信SPI方框图 SPI操作:通过使用SPI接口,S3C2440可以与外部器件同时发送、接收8位数据。当SPI接口为主机时,可以通过设置SPPREn寄存器来设置发送频率,当SPI为从机时,由其它主机提供时钟频率。当程序员写字节数据到SPTDATn寄存器,将同时开始发送和接受,在一些情况下,应该在写字节数据到SPT

    2022年5月2日
    41
  • python如何查看pip版本并且升级pip

    python如何查看pip版本并且升级pip第一次写 Python 的学习经历我之前也安装过 Python 今天 终于重新安装了 64 位的 windows 的 Python 于是在命令行输入 piplist 出现以下的提示 这个提示以前也出现过 但是看不懂 也不知道怎么处理 然后又胡乱安装 后来不知道怎么就安装 pip 成功了 并且还把 猜数字 游戏打包做成了 exe 文件 在同事的电脑上测试了下 竟然能成功运行 这个游戏名字叫 guessnumber 虽然很简单 感谢小甲鱼 这个程序是照着他的视频学的 但是却是我的处女作 它给了我继续学习 Python 的信心

    2026年3月17日
    1
  • java中的全局变量和局部变量_JVM中基本变量放在哪

    java中的全局变量和局部变量_JVM中基本变量放在哪正在使用NetBeans做一个稍大点的程序,刚刚学习使用,感觉程序中需要用到全局变量(其实java中是没有全局变量这个概念的)。在网上查了不少的资料,得到一点点体会:首先,java中是没有全局变量这个概念的,java程序中不能像C++那样在类外定义全局变量,因为JAVA当初出现的初衷就是为了安全性和跨平台性,所以去掉了类似C、C++中的全局变量的概念。JAVA中不应该有所谓全局变量的概念,全局变量…

    2022年8月21日
    9
  • 模糊聚类分析方法

    模糊聚类分析方法模糊数学模型系列博文 1 基本概念 隶属函数 模糊集合的表示方法 模糊关系 模糊矩阵 2 模糊模式识别 海明贴近度 欧几里得贴近度 黎曼贴近度 格贴近度 最大隶属原则 择近原则 3 模糊聚类分析方法 模糊等价矩阵 模糊相似矩阵 传递闭包法 布尔矩阵法 4 模糊决策分析方法在工程技术和经济管理中 常常需要对某些指标按照一定的标准 相似的程度或亲疏关系等 进行分类处理

    2026年3月26日
    1
  • Navicat 15 for MySQL 注册激活码_通用破解码

    Navicat 15 for MySQL 注册激活码_通用破解码,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月17日
    1.1K
  • explan各项说明

    explan各项说明explainselec fromuserexpl fromuseridSE 识别符 这是 SELECT 的查询序列号 select typeSELECT 类型 可以为以下任何一种 SIMPLE 简单 SELECT 不使用 UNION 或子查询 PRIMARY 最外面的 SEL

    2026年3月18日
    1

发表回复

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

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