一文阐明死锁的成因及解决方案

news2024/11/19 13:31:59

死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

目录

一、死锁的几种情况

1、一个线程,一把锁(上面同一线程给同一对象加两次锁的情况)

2、两个线程,两把锁

3、N个线程M把锁

二、造成死锁的4个必要条件⭐

三、如何避免死锁

1、加锁顺序-破除循环等待

2、资源分配策略-银行家算法(略)

3、超时机制

4、死锁检测与恢复

5、避免共享资源

四、面试题-死锁


一、死锁的几种情况

1、一个线程,一把锁(同一线程给同一对象加两次锁的情况)

可重入锁没事,不可重入锁可能死锁。

class BlockingQueue {
    synchronized void put(int elem){
        this.size();
        ...
    }
    
    synchronized int size() {
        ...
    }
}

补充:可重入锁与不可重入锁 

可重入锁

是一种支持同一线程多次获取该锁的锁。当线程第一次获取可重入锁后,可以多次重复获取该锁,而不会被自己所持有的锁所阻塞。

比如一个递归函数里有加锁操作,递归过程中这个锁会阻塞自己吗?如果不会,那么这个锁就是可重入(因为这个原因的可重入锁也叫做递归锁

Java里,只要以Reentrant开头命名的锁都是可重入锁,而且JDK提供的所有现成的Lock实现类,包括synchronized关键字锁都是可重入的。可重入锁在加锁时会判定看当前申请锁的线程是否已经是锁的拥有者,如果是,则直接放行。

不可重入锁

是一种不允许同一线程多次获取该锁的锁。当线程第一次获取不可重入锁后,再次尝试获取该锁时会被自己所持有的锁所阻塞。如果同一线程在获取不可重入锁后再次尝试获取该锁,会因“把自己锁死”而导致死锁。 

2、两个线程,两把锁

即使是可重入锁也可能会死锁。如下图情况,t1与t2并发执行。t1先对locker1加锁,t2先对locker2加锁;t1继续执行,又要对locker2加锁,但必须等待t2先释放locker2;t2继续执行,又要对locker1加锁,但必须等待t1先释放locker1。这时就发生了死锁。

这就相当于疫情时期你没带口罩准备去超时买口罩,但超市却说你没带口罩不让你进。

3、N个线程M把锁

线程的数量和锁的数量增多,就更容易造成死锁了。

这就涉及到那个著名的“哲学家就餐问题”:

显然,如果此时五位哲学家同时拿起左手边的筷子,就死锁了。也就是说,一个线程如果要加两次锁,如果已经加了一个,另一个被抢走了,它就会一直等待,同时占用着第一次加的锁。


二、造成死锁的4个必要条件⭐

  1. 互斥使用:即当资源被一个线程使用(占有)时,别的线程不能使用。【锁的基本特点】
  2. 不可抢占:资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。【锁的基本特点】
  3. 请求和保持:即当资源请求者在请求其他的资源的同时,保持对原有资源的占有。(吃着碗里的看着锅里的。)(没拿到第二根筷子时也不会放弃前一根。)【代码的特点】
  4. 循环等待:即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个逻辑依赖循环的等待环路。(家钥匙锁车里了,车钥匙锁家里了。)(上面的哲学家中,5等待4,4等待3,……,2等待1,1又等待5)【代码的特点】

三、如何避免死锁

避免死锁的方法有很多,这里只列举出5种。其中,重点阐述第一种:加锁顺序。

1、加锁顺序-破除循环等待

当上述四个条件都成立的时候形成死锁。死锁的情况下,如果打破上述任何一个条件,便可让死锁消失。 其中最容易破坏的是 “循环等待”。

一个简单的破解循环等待情况的方法是:针对锁进行编号,如果需要同时获取多把锁,约定加锁顺序务必是先对小的编号加锁后对大的编号加锁。

如下图,约定每个哲学家只能先获取左手和右手中编号较小的筷子。2号哲学家先获取1号筷子,剩下的哲学家也依次获取左右手之间较小的筷子,轮到最后一位1号哲学家时,由于1号筷子已经被占用,他就无法获取1号筷子,而进入阻塞等待。

在1号哲学家阻塞等待时,5号哲学家就能拿起5号筷子,开始吃面。当他完成任务后,放下4号和5号两根筷子,此时4号哲学家又能拿起4号筷子吃面。依次地,3号、2号哲学家也能完成吃面。等到2号哲学家放下筷子后,1号哲学家可以获取到1号筷子和5号筷子,从而结束阻塞等待,开始执行吃面任务。

因此,只要约定了加锁顺序,循环等待条件就会自然破除,死锁也就不会形成了。体现在代码中,只要是一个线程中要加多把锁,就一定要注意加锁的顺序。可以约定每次加锁的时候都先给编号小的加锁,后给编号大的加锁,并且所有的线程都遵循这个顺序即可。

如下图所示,只要每次加锁的时候都先给locker1加锁,后给locker2加锁即可。(把线程加锁的顺序都固定。)

2、资源分配策略-银行家算法(略)

可以采用银行家算法(Banker's Algorithm)等资源分配策略,通过预先评估资源的最大需求量和可用量,确保系统分配资源时不会导致死锁的发生。

银行家算法的本质是对资源更合理的分配。它在学校操作系统课会学,也是期未考试必考题。

但其实本身比较复杂,实现这个算法本身还可能引入额外的 bug,得不偿失,因此不适合实际开发中使用。这里就不展开来说了。

3、超时机制

为获取锁操作设置一个超时时间,在等待超过一定时间后放弃获取锁,并进行相应的处理,避免长时间等待造成系统阻塞。

4、死锁检测与恢复

通过周期性地检测系统中是否存在死锁,并采取相应的措施进行恢复,例如终止某些进程或回滚操作。

5、避免共享资源

尽量减少进程间共享资源的数量,或者采用副本而不是共享资源的方式,避免资源竞争导致死锁的可能性。


四、面试题-死锁

谈谈死锁是什么,如何避免死锁,避免算法? 实际解决过没有?

总结:

死锁指的是两个或多个线程(或进程,以下只表述线程)无限地等待对方持有的资源,导致程序无法继续执行的状态。

死锁发生的四个必要条件是:互斥条件(资源在任意时刻只能被一个线程占用),请求与保持条件(线程在持有资源的同时也在等待获取其他线程持有的资源),不可抢占条件(线程不能被强行抢占已被别的线程占用的资源),循环等待条件(每个线程都在等待下一个线程所持有的资源)。

为了避免死锁,可以采取固定加锁顺序,调整资源分配策略,设置超时释放锁的时间,周期性检测死锁,避免共享资源等策略。避免算法是一种预防死锁的方法,其中最著名的算法是银行家算法。银行家算法基于资源分配的安全性,确保在分配资源时不会导致死锁的发生。

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

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

相关文章

stm32PID调参实验

使用了增量式PID,有点难调,数据一直在波动。实际中调参具有很大的不确定性,因为你这次调好了,下次再供电发现又不是那么回事。按照网上传统的调参方法,一般是先调P,I和D为0,逐渐增大P使得实际值快速到达目标…

【API生命周期看护】API监控与运维

一、基本概念 当接口完成发布、上线后,就进入了正常的运行与维护状态。此时,对于API本身的监控与运维则变得尤为重要,这是保障服务功能可用、SLA达成的重要手段。 监控与运维本身是一个非常大的概念,从DevOps这一词汇中也能看出…

Linux下做性能分析6:理解一些基础的CPU执行模型

[介绍] 前面介绍了两个典型的调度模型,如果调度没有问题,剩下的问题就是正面刚算法了。那个不是我这里要介绍的主题的。 但,Not Really。其实除了算法在消耗CPU,CPU还是有很多余力可以挖掘的,这一篇我们专门讨论一下…

MySQL “error: ‘fd’: 未知重写说明符”

文章目录 1、【问题】MySQL “error: ‘fd’: 未知重写说明符”2、【解决】增加引用头文件 1、【问题】MySQL “error: ‘fd’: 未知重写说明符” 有以下代码,mysql 的 include 和 lib 都已经加入附加目录,libmysql.lib 已经加入依赖库: #p…

IEEE ICME 2023论文|基于交互式注意力的语音情感识别联合网络

论文题目: A Joint Network Based on Interactive Attention for Speech Emotion Recognition 作者列表: 胡英,侯世静,杨华敏,黄浩,何亮 研究背景 语音情感识别(Speech Emotion Recognitio…

4. MySQL 的增删查改(重点 9000字详解)

目录 准备工作 一、数据的插入 (insert) 注意 1. 整行插入 2. 指定列的插入(常用) 3. 一次插入多行数据 4. 清空数据库的数据(truncate) 5. 拓展练习:对于数据库中的数据进行统计&#…

多智能体强化学习(MARL)研究汇总:行为分析、通信学习、协作学习、智能体建模

【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等、趣味项目实现、学术应用项目实现 专栏详细介绍:【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等、趣味项目实现、学术应…

第一百零七天学习记录:C++核心:类和对象Ⅷ(五星重要)多态

多态 多态的基本概念 多态是C面向对象三大特性之一 多态分为两类 1、静态多态:函数重载 和 运算符重载属于静态多态,复用函数名 2、动态多态:派生类和虚函数实现运行时多态 静态多态和动态多态的区别: 1、静态多态的函数地址早绑…

Arthas的火焰图生成

之前说过用idea自带的工具生成火焰图,但是idea是在本地的,在机器上如何生成呢? 我觉得方法有很多,这里用arthas工具简单搞一搞 Arthas官网地址 下载Arthas 如果在机器上要下载整个包,arthas-boot.jar需要依赖其他的j…

上门按摩系统如何运营才能做大

预约上门按摩系统是一种在线平台或应用程序,用于帮助用户预约并安排专业按摩师上门提供按摩服务。这种系统通常为用户提供一个简便的方式来选择按摩服务类型、时间和地点,并与合适的按摩师进行预约。用户可以通过应用程序或网站浏览按摩师的资料和评论&a…

用googletest写cpp单测

框架概述 Google Test(也称为 googletest)是由 Google 开发的 C 单元测试框架。它的首个版本是在2004年发布的,作为 Google 内部的测试框架使用。随后,Google Test 在开源社区中得到广泛应用,并在许多项目和组织中成为…

总结927

今晚用了40分钟进行回顾,但这40分钟,能回顾一天所学?一共四门课,每门用10分钟回顾,光是书籍,资料的切换都需要30秒。10分钟回顾对于政治来说是足够的,但对于数学,能重做2~3道题就很不…

centos环境搭建nsq单点

简言 下载 启动nsq(单节点) 1. 启动nsqd 2. 启动nsqlookupd 3. 启动nsqadmin 查看状态 简言 1. nsq是go语言实现的分布式消息处理平台,类似我们常用的kafka,rocket mq等,目的是用来大规模地处理每天数以十亿计级别的消息。它具有分布式和…

前端:UI 交互式特效 —— Css、Js

😷😊🤺🤺🤺前期回顾 打造极简风格动效 —— 5 分钟轻松实现惊艳、震撼人心的视觉效果_彩色之外的博客-CSDN博客 😁 css动画 —— 把你喜欢css动画嵌入到浏览器中_css做的动画效果怎么嵌入网页_彩色之外的…

研0进阶式学习—-数据挖掘概念与技术

目录 【 写在前面】什么是数据挖掘为何进行模式评估如何进行模式评估数据挖掘的发展趋势 【 写在前面】 本科期间,数据挖掘算法学过一些,甚至本人的毕业设计也是围绕此展开的,但是显然学得太皮毛,今天偶然读到《数据挖掘•概念与…

基于tauri+vue3+pinia2客户端管理系统程序|tauri+vite4后台系统

TauriAdmin一款跨端通用后台系统模板解决方案 基于 tauri rust webview2 整合 vite4 搭建桌面端 vue3 管理后台模板TauriVue3Admin。支持多窗口切换管理、vue-i18n多语言、动态路由权限、常用业务功能模块及动态路由缓存等功能。 使用技术 编码工具:Vscode框架技术…

【chap4-链表】用Python3刷《代码随想录》

通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域data,另一个是指针域next(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针) 链接的入口点称为…

耳夹式骨传导耳机有哪些比较好用?这三个款式不容错过!

骨传导耳机由于不入耳,不用担心耳道健康问题,越来越受到广大网友的喜欢,而传统的入耳式耳机,则因为长时间佩戴会耳朵痛,容易掉落等问题逐渐的被网友抛弃,那么在骨传导耳机市场种类这么多的情况下&#xff0…

Apache Kudu 在**医疗科技的生产实践

目录 说明 医疗场景下数据特点 KUDU 的介绍 kudu 架构 kudu 文件组织形式 kudu的生产实践 技术选型 整体的架构 项目遇到的问题 参考资料 说明 本文主要介绍APACHE KUDU 在**医疗科技数据实时分析场景下的实践,内容包括: 医疗场景下数据特点 …

mysql什么情况下行锁(表锁)(锁的概念)

1:数据表aa的设计结构 2: 使用navicat编写手动控制事务 3:先选择开启事务和执行更新操作,where b1(表锁)b不是索引,不提交事务,(如果where b1,b是索引就行锁&…