死锁的原因及解决方法

news2024/9/29 17:27:52

❣️关注专栏: JavaEE


死锁

  • ☘️1.什么是死锁
  • ☘️2.死锁的三个典型情况
    • ☘️2.1情况一
    • ☘️2.2情况二
    • ☘️2.2.1死锁的代码展示
    • ☘️2.3多个线程多把锁
  • ☘️3死锁产生的必要条件
    • ☘️3.1互斥性
    • ☘️3.2不可抢占
    • ☘️3.3请求和保持
    • ☘️3.4循环等待
  • ☘️4如何避免死锁
    • ☘️4.1避免死锁代码

☘️1.什么是死锁

死锁是一个非常让程序猿烦恼的问题,一旦所写的程序有了死锁,那么程序就无法执行下去,会出现严重的 bug,并且死锁非常隐蔽,我们不会轻易发现它,在开发阶段,不经意期间我们就会写出死锁,很难检测出来。
那么什么是死锁呢?竟然让我们如此烦恼。
“死锁”就是2个或2个以上的线程互相持有对方想要的资源,导致各自处于阻塞等待状态,致使程序无法执行下去,这就是“死锁”。

☘️2.死锁的三个典型情况

☘️2.1情况一

一个线程一把锁,连续加两次。如果锁是不可重入锁,就会死锁。
Java 里的 synchronized 和 ReentrantLock 都是可重入锁

☘️2.2情况二

两个线程两把锁,t1 和 t2 各自对 锁A 和 锁B 加锁,再尝试获取对方的锁。线程在竞争资源,导致死锁。
下面图解展示以下这种情况:
在这里插入图片描述

☘️2.2.1死锁的代码展示

/**
     * 死锁
     * 两个线程两把锁,一个线程各对应一把锁,再获取另一个锁
     */
    public static void main(String[] args) {
        Object jiangyou = new Object();
        Object cu = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (jiangyou) {
                // 加入 sleep 都是为了让线程先把对应的第一个锁拿到
                // 加上休眠,表示让 jiangyou 的锁获取到之后,再获取 cu 的锁
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (cu) {
                    System.out.println("t1把酱油和醋都拿到了");
                }
            }
        });
        Thread t2 = new Thread(() -> {
            synchronized (cu) {
                // 加上休眠,表示让 cu 的锁获取到之后,再获取 jiangyou 的锁
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (jiangyou) {
                    System.out.println("t2把醋和酱油都拿到了");
                }
            }
        });

        t1.start();
        t2.start();
    }

运行之后发现无法打印出结果,那么我们查看一下线程调用栈,按照这个路径查找到线程调用栈,我这里的jdk版本如下是jdk1.8.0_192,不同的版本名称不一样,你是 jdk1.9.0就点开 jdk1.9.0就行了。
在这里插入图片描述
点击jconsole.exe,按照以下步骤进行连接:有人可能不会出现第一步(第一步的这个名称是我 idea 中这个代码的类名),因为你可能没有运行该代码,你要保持这个代码一直在运行,不要停止运行,就会出现这个连接。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
同样点开 Thread-1 也会有这样的描述。
针对这样的死锁问题,需要借用这个 jconsole.exe 工具来帮助我们定位到死锁的地方。

☘️2.3多个线程多把锁

我们在学校学习的时候,最经典的就是哲学家就餐问题。一共有5个哲学家(A、B、C、D、E),一共有5根筷子(1、2、3、4、5),共同吃一碗面条。
在这里插入图片描述
每个哲学家有两种状态:
(1)思考问题(相当于线程中的阻塞状态)
(2)拿起筷子吃面条(相当于线程获取到锁之后执行一些计算)

由于操作系统是随时调度的,所以这五个哲学家,随时都可能想要吃面条,也随时都可能思考问题。正常来说,如果想要吃面条,需要拿起左手和右手的两根筷子。
假设此时出现了特殊情况:同一时刻,所有的哲学家都拿起了左手的筷子,需要吃面还需要拿起右手的筷子,就需要等待右边的哲学家把筷子放下,此时就是出现了死锁。

☘️3死锁产生的必要条件

☘️3.1互斥性

线程1 拿到了锁,线程2 就必须等着,所以线程之间是互斥使用锁的。

☘️3.2不可抢占

线程1 拿到了锁,必须是 线程1 主动释放,线程2 才能得到锁,如果 线程1 不主动释放锁,线程2 是不能强行获取到锁的。

☘️3.3请求和保持

线程1 拿到 锁A 之后,再尝试获取 锁B,A 这把锁还是保持的,不会因为 线程1 想要获取 锁B 就把 锁A 释放了。

☘️3.4循环等待

线程1 拿到 锁A 之后,尝试获取 锁B,线程2 拿到 锁B 之后,尝试获取锁A。
线程1 在获取 锁B 的时候等待 线程2 释放 B,同时线程2 在获取 A 的时候需要等待线程1 释放 A。

这4个必要条件缺一不可。前3点都是锁的基本特性,一般不需要我们程序猿自己去设置,只有第4个是唯一一个和代码相关的,也是程序猿可以控制的。

☘️4如何避免死锁

想要避免死锁,我们就必须打破必要条件,这里的突破口就是第四点的循环等待这一条件,我们可以给锁进行编号,然后制定一个固定的顺序(比如从小到大)来加锁。任意个线程加多把锁的时候都让线程按照上边的规则,就是按照固定的顺序加锁,自然循环等待的条件就会被打破,此时就会避免死锁了。

☘️4.1避免死锁代码

/**
     * 解决死锁:指定一个固定的顺序
     * 比如:都先拿 jiangyou 再拿 cu
     */
    public static void main1(String[] args) {
        Object jiangyou = new Object();
        Object cu = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (jiangyou) {
                // 加入 sleep 都是为了让线程先把对应的第一个锁拿到
                // 加上休眠,表示让 jiangyou 的锁获取到之后,再获取 cu 的锁
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (cu) {
                    System.out.println("t1把酱油和醋都拿到了");
                }
            }
        });
        Thread t2 = new Thread(() -> {
            synchronized (jiangyou) {
                // 加上休眠,表示让 cu 的锁获取到之后,再获取 jiangyou 的锁
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (cu) {
                    System.out.println("t2把醋和酱油都拿到了");
                }
            }
        });

        t1.start();
        t2.start();
    }

因为指定了一个顺序:都先拿 jiangyou(酱油),再拿 cu(醋)。让线程 t1 把酱油和醋拿到之后就会释放锁,然后线程 t2 就会去获取,从而避免了循环等待对方了。运行结果如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/339020.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Spark分布式内存计算框架——Spark Core】6. RDD 持久化

3.6 RDD 持久化 在实际开发中某些RDD的计算或转换可能会比较耗费时间,如果这些RDD后续还会频繁的被使用到,那么可以将这些RDD进行持久化/缓存,这样下次再使用到的时候就不用再重新计算了,提高了程序运行的效率。 缓存函数 可以…

Kubernetes集群-部署Java项目

Kubernetes集群-部署Java项目(SSG) k8s部署项目java流程图 第一步 打包制作镜像 打包 java源码: application.properties #在有pom.xml的路径下执行 mvn clean package制作镜像: 将刚才打包后的文件夹传到,装有dock…

(考研湖科大教书匠计算机网络)第三章数据链路层-第十一节:虚拟局域网VLAN概述和实现机制

获取pdf:密码7281专栏目录首页:【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一:VLAN概述(1)分割广播域(2)虚拟局域网VLAN二:VLAN实现机制(1)IEE…

LeetCode题目笔记——15. 三数之和

文章目录题目描述题目链接题目难度——中等方法一:暴力(参考)代码/Python 参考方法二:哈希代码/Python参考方法三:排序双指针代码/Python代码/C总结题目描述 龙门阵:这个n个数之和是不是有什么深意啊&#…

Python中的类和对象(6)

文章目录1.多态2.类继承的多态3.自定义函数的多态4.鸭子类型5.思维导图1.多态 多态在编程中是一个非常重要的概念,它是指同一个运算符、函数或对象在不同的场景下,具有不同的作用效果,这么一个技能。 我们知道加号()…

加载sklearn新闻数据集出错 fetch_20newsgroups() HTTPError: HTTP Error 403: Forbidden解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。喜欢通过博客创作的方式对所学的知识进行总结与归纳,不仅形成深入且独到的理…

大数据框架之Hadoop:入门(五)Hadoop编译源码(面试重点)

5.1 前期准备工作 1.CentOS联网 配置CentOS能连接外网。Linux虚拟机ping www.baidu.com 是畅通的 注意:采用root角色编译,减少文件夹权限出现问题 2.jar包准备(hadoop源码、JDK8、maven、ant 、protobuf) (1)hadoop-2.7.7-sr…

【TCP的拥塞控制】基于窗口的拥塞控制

TCP的拥塞窗口CWND大小和传输轮次n的关系如下所示。(本题10分) cwnd12481632333435363738394041422122232425261248N1234567891011121314151617181920212223242526 问题: (1)慢开始阶段的时间间隔?&#…

NFC enable NFC使能流程

同学,别退出呀,我可是全网最牛逼的 WIFI/BT/GPS/NFC分析博主,我写了上百篇文章,请点击下面了解本专栏,进入本博主主页看看再走呗,一定不会让你后悔的,记得一定要去看主页置顶文章哦。 NFC enable NFC使能流程 认识nfc系统如何工作,最好的方法就是了解nfc的各个流程,…

linux系统下SVN服务器搭建

linux新手,整了好几天才搞好,做下笔记以备后续使用: 1、下载svn服务器 yum -y install subversion 2、创建仓库 svnadmin create /opt/svn/pro/respos1 svnadmin create /opt/svn/pro/respos2 3、配置用户以及权限 1:cd到仓库目录下&#…

py3常用返回规则字符串的函数+ascii与char的转换

文章目录py3常用返回规则字符串的函数字符转ascii以及ascii转字符的方法为:py3常用返回规则字符串的函数 注明原来的网址为:https://docs.python.org/3.8/library/string.html string.ascii_letters 返回所有的大写、小写字母 string.ascii_lowercase 返…

寒假安全作业nginx-host绕过实例复现

1.测试环境搭建 LNMP架构的话,肯定就是linux、nginx、mysql、php四大组件。在后面的复现中我们还会用到https的一部分知识,故这里的nginx就需要使用虚拟主机并且配置https证书,且具有php解析功能。 1.1 基础nginx配置 #1.创建web目录 mkdir …

MySQL-InnoDB行格式浅析

简介 我们知道读写磁盘的速度非常慢,和内存读写差了几个数量级,所以当我们想从表中获取某些记录时, InnoDB 存储引擎需要一条一条的把记录从磁盘上读出来么? 不,那样会慢死,InnoDB 采取的方式是&#xff1a…

雅思经验(十三)

感觉这篇其实有点小难,我在精听的才发现那是六个实验对象,但是叫做six subjects在精听的时候感觉有些手忙脚乱,像是一团乱麻一样,但是也是没有什么关系。其实这才是查漏补缺,cello player 这是大提琴师violinists 小提…

08- 数据升维 (PolynomialFeatures) (机器学习)

在做数据升维的时候,最常见的手段就是将已知维度进行相乘(或者自乘)来构建新的维度 使用 np.concatenate()进行简单的,幂次合并,注意数据合并的方向axis 1 数据可视化时,注意切片,因为数据升维…

SpringDI自动装配BeanSpring注解配置和Java配置类

依赖注入 上篇博客已经提到了DI注入方式的构造器注入&#xff0c;下面采用set方式进行注入 基于set方法注入 public class User {private String name;private Address address;private String[] books;private List<String> hobbys;private Map<String,String>…

【Linux】多线程编程 - 概念/pthread库调用接口/互斥

目录 一.线程概念 1.Linux中线程如何实现 2.POSIX线程库: pthread第三方线程库 3.线程与进程的数据存放问题 4.线程的"高效"具体体现在哪 5.线程优缺点 二.线程控制 0.关于pthread_t类型 1.线程创建 2.线程终止 3.线程等待 4.线程分离 5.代码验证 三.线…

你期待已久的《动手学深度学习》(PyTorch版)来啦!

《动手学深度学习》全新PyTorch版本&#xff0c;李沐和亚马逊科学家阿斯顿张等大咖作者强强联合之作&#xff0c;机器学习、深度学习领域重磅教程&#xff0c;交互式实战环境&#xff0c;配套资源丰富&#xff01; 面向中文读者的能运行、可讨论的深度学习入门书。 动手学深度…

一个优质软件测试工程师简历的范文(一定要收藏)

很多刚转行软件测试的小伙伴是不是不知道怎么写好一份优质的软件测试工程师的简历。今天呢&#xff0c;就给大家分享一下一个优质软件测试工程师简历的范文。记得收藏起来哦。 下面的案例&#xff1a;2-3年的软件测试工程的简历 姓 名&#xff1a;XXX 学历&#xff1a…

2023备战金三银四,Python自动化软件测试面试宝典合集(五)

接上篇八、抓包与网络协议8.1 抓包工具怎么用 我原来的公司对于抓包这块&#xff0c;在 App 的测试用得比较多。我们会使用 fiddler 抓取数据检查结果&#xff0c;定位问题&#xff0c;测试安全&#xff0c;制造弱网环境;如&#xff1a;抓取数据通过查看请求数据&#xff0c;请…