【多线程初阶】死锁的产生 如何避免死锁

article/2025/6/21 4:36:40

文章目录

  • 关于死锁
    • 一.死锁的三种情况
      • 1.一个线程,一把锁,连续多次加锁
      • 2.两个线程,两把锁
      • 3.N个线程,M把锁 --哲学家就餐问题
    • 二.如何避免死锁
      • 死锁是如何构成的(四个必要条件)
      • 打破死锁
    • 三.死锁小结

关于死锁

一.死锁的三种情况

在这里插入图片描述

  • 1.一个线程,一把锁,连续多次加锁 -->由synchronized 锁解决
  • 2.两个线程,两把锁,每个线程获取到一把锁之后,尝试获取对方的锁
  • 3.N个线程,M把锁 -->一个经典的模型,哲学家就餐问题

1.一个线程,一把锁,连续多次加锁

在这里插入图片描述

一个线程,一把锁,连续多次加锁,在实际学习和工作中,是很容易被写出来的,一旦方法调用的层次比较深,就搞不好容易出现这样的情况,想要解除阻塞,需要 往下执行,想要往下执行,就需要等待第一次的锁被释放,这样就形成了死锁(dead lock),就如同下面的Demo18,一个线程对同一把锁进行多次加锁,但是运行出来结果没错

为了解决当方法调用层次比较深出现一个线程,一把锁,多次加锁形成死锁的情况,Java中的synchronized 就引入了可重入概念,在上一篇博客 synchronized关键字里有详细解释,本篇博客不再赘述

代码示例:

class Counter{private int count = 0;synchronized public void add(){count++;}public int get(){return count;}public synchronized  static void func(){synchronized (Counter.class){}}}
public class Demo18 {public static void main(String[] args) throws InterruptedException {Object locker = new Object();Counter counter = new Counter();Thread t1 = new Thread(() ->{for (int i = 0; i < 50000; i++) {counter.add();}});Thread t2 = new Thread(() ->{for (int i = 0; i < 50000; i++) {counter.add();}});t1.start();t2.start();t1.join();t2.join();System.out.println("count="+counter.get());}
}

在这里插入图片描述

2.两个线程,两把锁

两个线程,两把锁,每个线程获取到一把锁之后,尝试获取对方的锁

用生活中的实际场景,举例说明:
比如,吃饺子~~,朝新喜欢蘸酱油吃,小舟喜欢蘸醋吃,后来两人都习惯了对方的习惯,两人都是同时蘸醋和酱油吃饺子,朝新拿起酱油,小舟拿起醋
朝新说:你把醋给我,我用完了,全都给你
小舟说:不行,你把酱油先给我,我用完了,全都给你

此时两个线程互不相让,就会构成死锁~~
还比如,房钥匙锁车里了,车钥匙锁家里了

代码示例:

public class Demo20 {public static void main(String[] args) throws InterruptedException {Object lock1 = new Object();Object lock2 = new Object();Thread t1 =new Thread(() ->{synchronized (lock1){//朝新拿起酱油try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//朝新尝试拿起醋synchronized (lock2){System.out.println("t1 线程两个锁都获取到");}}});Thread t2 =new Thread(() ->{synchronized (lock2){//小舟拿起醋try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//小舟尝试拿起酱油synchronized (lock1){System.out.println("t2 线程两个锁都获取到");}}});t1.start();t2.start();t1.join();t2.join();}
}

在这里插入图片描述

必须是,拿到第一把锁,再拿第二把锁(不能释放第一把锁)

在这里插入图片描述
其中加入sleep的作用:

在这里插入图片描述
加入sleep就是为了确保上述错误代码构成死锁

如果让我们手写一个出现死锁的代码,就是要通过上述代码,写两个线程两把锁,注意要精确控制好加锁的顺序,不进行控制的话,随机调度就有可能不构成死锁了

3.N个线程,M把锁 --哲学家就餐问题

在这里插入图片描述

大部分情况下,上述模型,可以很好的运转,但是在一些极端情况下会造成死锁
像是,同一时刻,大家都想吃面条,同时拿起左手的筷子,此时任何一个线程都无法拿起右手的筷子,任何一个哲学家都吃不成面条
每个线程,都不会放下手里的筷子,而是阻塞等待,构成死锁

上述场景虽说非常极端,但是在以后的学习和工作中,比如我们以后会做服务器开发,同时为很多个用户提供服务,假设上述场景,即使出现死锁的概率是1%%,服务器可能一天要处理几千万的请求(比如百度,一天要处理10亿量级的请求),这样就会出现10万次死锁情况,就比如温总理说的:在咱们国家,再小的问题,乘以13亿都是大问题~~,那么如何避免死锁问题呢?

二.如何避免死锁

死锁是如何构成的(四个必要条件)

在这里插入图片描述

  • 1.锁是互斥的,一个线程拿到锁之后,另一个线程再尝试获取锁,必须要阻塞等待 (锁的基本性质)
  • 2.锁是不可抢占的(不可剥夺),线程1拿到锁 线程2也尝试获取这个锁,线程2必须阻塞等待,而不是线程2直接把锁抢过来 (锁的基本特性)
  • 3.请求和保持,一个线程拿到锁1 之后不释放锁1的前提下,去获取锁2
  • 4.循环等待,多个线程,多把锁之间的等待过程,构成了"循环",A等待B,B等待C,C等待A

以上四个形成死锁的必要条件,其中1和2都是锁自己的基本性质和特性,至少,Java中的synchronized锁是遵守这两点的,各种语言中内置的锁/主流的锁,都是遵守这两点的,这两点我们改变不了

只要破坏上述的3 ,4任何一个条件都能够打破死锁

打破死锁

  • 1.打破必要条件3 :请求和保持
    如果是先放下左手的筷子,再去拿右手的筷子,就不会构成死锁了,也就是代码中加锁的时候,不要"嵌套加锁"

代码示例:

public class Demo20 {public static void main(String[] args) throws InterruptedException {Object lock1 = new Object();Object lock2 = new Object();Thread t1 =new Thread(() ->{synchronized (lock1){//朝新拿起酱油try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}//朝新尝试拿起醋synchronized (lock2){System.out.println("t1 线程两个锁都获取到");}});Thread t2 =new Thread(() ->{synchronized (lock2){//小舟拿起醋try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}//小舟尝试拿起酱油synchronized (lock1){System.out.println("t2 线程两个锁都获取到");}});t1.start();t2.start();t1.join();t2.join();}
}

在这里插入图片描述
这种破坏死锁的方法不够通用,有些情况下,确实需要拿到多个锁,再进行某个操作的(嵌套,很难避免)

  • 2.打破必要条件4 :循环等待

约定好加锁的顺序,就可以破除循环等待了,我们约定好,每个线程加锁的时候,永远是先获取序号小的锁,后获取序号大的锁

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

通过上述哲学家就餐模型,我们可以观察到,只要规定好加锁的顺序,就可以打破循环等待,从而避免死锁问题

我们使用上述吃饺子过程中出现的死锁问题来观察,通过破除循环等待,也就是规定好加锁顺序后,是如何避免死锁问题的

public class Demo20 {public static void main(String[] args) throws InterruptedException {Object lock1 = new Object();Object lock2 = new Object();Thread t1 =new Thread(() ->{synchronized (lock1){//朝新拿起酱油System.out.println("t1 拿到locker1");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//朝新尝试拿起醋synchronized (lock2){System.out.println("t1 线程两个锁都获取到 吃面条");}}});Thread t2 =new Thread(() ->{synchronized (lock1){//小舟拿起醋System.out.println("t2 拿到locker1");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//小舟尝试拿起酱油synchronized (lock2){System.out.println("t2 线程两个锁都获取到 吃面条");}}});t1.start();t2.start();t1.join();t2.join();}
}

在这里插入图片描述

三.死锁小结

在这里插入图片描述


http://www.hkcw.cn/article/exSZtWrJYZ.shtml

相关文章

第3节 Node.js 创建第一个应用

Node.js 非常强大&#xff0c;只需动手写几行代码就可以构建出整个HTTP服务器。事实上&#xff0c;我们的Web应用以及对应的Web服务器基本上是一样的。 在我们创建Node.js第一个"Hello, World!"应用前&#xff0c;让我们先了解下Node.js应用是由哪几部分组成的&…

余承东:ADS 4今年Q4实现高速L3商用,展望2026年L4

2025(第三届)未来汽车先行者大会于5月31日至6月1日在深圳国际会展中心(宝安)举行,华为常务董事、终端BG董事长余承东在会上发表了演讲。余承东在演讲中提到ADS的进展和展望。他表示,尊界S800首发搭载了ADS 4,并预计从2025年第三季度开始,尊界S800等车型将升级到ADS 4。…

Redis实战-基于redis和lua脚本实现分布式锁以及Redission源码解析【万字长文】

前言&#xff1a; 在上篇博客中&#xff0c;我们探讨了单机模式下如何通过悲观锁&#xff08;synchronized&#xff09;实现"一人一单"功能。然而&#xff0c;在分布式系统或集群环境下&#xff0c;单纯依赖JVM级别的锁机制会出现线程并发安全问题&#xff0c;因为这…

【本周开启】Springer |第七届区块链、人工智能和可信系统国际会议(BlockSys‘2025)

Springer |第七届区块链、人工智能和可信系统国际会议&#xff08;BlockSys2025&#xff09; International Conference on Blockchain, Artificial Intelligence, and Trustworthy Systems 中国 珠海 2025年05月30日-2025年05月31日 大会官网&#xff1a;BlockSys2025 – Int…

PCIE硬件管脚顺序问题解决方案

当你的硬件设计的管脚顺序不对&#xff0c;或者tx/rx的顺序搞反了&#xff0c;您发现你在自己工程的XDC中对管脚进行分配&#xff0c;布线通不过&#xff0c;或者布过去了&#xff0c;上位机不能识别&#xff0c;这个是由于综合工具默认是就近原则&#xff0c;使用自定义生成的…

生成https 证书步骤

一、OpenSSL下载 OpenSSL下载地址&#xff1a; https://slproweb.com/products/Win32OpenSSL.html 如果电脑是64位的就选择64位的 二、OpenSSL安装 双击打开.exe文件 开始安装&#xff0c;一直下一步&#xff0c;不过需要注意的是默认安装路径是C盘&#xff0c;可更改到其他盘…

Baklib内容中台革新企业知识实践

Baklib智能知识中枢构建 作为现代企业知识管理的核心架构&#xff0c;Baklib内容中台通过整合多源异构数据形成智能化知识中枢&#xff0c;实现从信息采集到价值转化的全链路管理。其底层采用跨平台数据贯通技术&#xff0c;支持API接口与企业现有CRM、ERP系统无缝对接&#x…

第六十三节:深度学习-模型推理与后处理

深度学习模型训练完成后,如何高效地将其部署到实际应用中并进行准确预测?这正是模型推理与后处理的核心任务。OpenCV 的 dnn 模块为此提供了强大支持,本文将深入探讨 OpenCV 在深度学习模型推理与后处理中的关键技术与实践。 第一部分:基础概念与环境搭建 1.1 核心概念解析…

【CF】Day71——⭐Codeforces Round 892 (Div. 2) D (二分 + 思维 + 差分模拟区间合并)

D. Andrey and Escape from Capygrad 题目&#xff1a; 思路&#xff1a; 很有思维的一题&#xff0c;非常nice 题目给了我们一个很有意思的条件&#xff0c;如果 x 在 l[i] ~ r[i] 之间&#xff0c;那么就可以跳跃到 a[i] ~ b[i] 之间&#xff0c;那么一个很显然的想法&#…

C#集合循环删除某些行

你想要在遍历集合&#xff08;例如List&#xff09;的同时删除某些元素时&#xff0c;直接在循环中删除元素可能会导致问题&#xff0c;因为这可能会改变集合的大小和导致索引问题&#xff1b; 可以用for循环的倒序来删除&#xff1b; 如果要删除满足特定条件的所有元素&…

fpga系列 HDL : FPGA实现奇数倍分频

偶分频 //240个时钟周期翻转一次输出&#xff0c;这实际上是一个 480倍分频器 reg [7:0] counter 0; reg divided_clk 1b0; always (posedge clk) beginif (counter 239) begin // 240counter < 0;divided_clk < ~divided_clk; end else begincounter < counter 1…

ICML 2025 Spotlight | 机器人界的「Sora」!让机器人实时进行未来预测和动作执行!

标题&#xff1a;Video Prediction Policy: A Generalist Robot Policy with Predictive Visual Representations 作者&#xff1a;Yucheng Hu, Yanjiang Guo, Pengchao Wang, Xiaoyu Chen, Yen-Jen Wang, Jianke Zhang, Koushil Sreenath, Chaochao Lu, Jianyu Chen 机构&am…

「 扑翼飞行器 」悬停飞行的信号串联滤波器设计

一、前言 小白在设计扑翼飞行器悬停算法过程中,设计了三种滤波器串联使用,总结如下。 二、正文 陷波滤波器 (Notch @30 Hz) 目的:针对扑翼机构或传感系统中常见的机械谐振或结构共振噪声进行有源抑制。 工作原理:在归一化频率 (假设采样率 , HZ)处设计一个陷波(notch)…

RL 基础 (待补充)

注&#xff1a;本文仅用于自学习笔记备忘&#xff0c;不做任何分享和商业用途。 主要参考资料&#xff1a; 蘑菇书EasyRLA (Long) Peek into Reinforcement Learning | LilLog 第1章 强化学习基础 RL算法分类&#xff1a; Model-based: Rely on the model of the environm…

Redis7底层数据结构解析

redisObject 在 Redis 的源码中&#xff0c;Redis 会将底层数据结构&#xff08;如 SDS、hash table、skiplist 等&#xff09;统一封装成一个对象&#xff0c;这个对象叫做 redisObject&#xff0c;也简称 robj。 typedef struct redisObject {unsigned type : 4; // 数…

Kafka 的 ISR 机制深度解析:保障数据可靠性的核心防线

在 Kafka 的消息处理体系中&#xff0c;数据的可靠性和高可用性是至关重要的目标。而 ISR&#xff08;In-Sync Replicas&#xff0c;同步副本&#xff09;机制作为 Kafka 实现这一目标的关键技术&#xff0c;在消息复制、故障容错等方面发挥着核心作用。接下来&#xff0c;我们…

cusor无限续杯

githut开源网址&#xff1a;https://github.com/yuaotian/go- 敲黑板下面是主要步骤和注意事项&#xff01; step1:cursor软件退出登录 step2:cursor网页端删除账号 step3:运行命令&#xff08;注意&#xff1a;用管理员身份运行windows powershell&#xff0c;不能用cmd&…

360浏览器设置主题

设置默认主题&#xff1a; 1.右上角有个皮肤按钮 进来后&#xff0c;右边有个回复默认皮肤按钮。 换成彩色皮肤后&#xff0c;找按钮不太好找了。

DAY 17 常见聚类算法

目录 DAY 17 常见聚类算法1.聚类的指标2.聚类常见算法&#xff1a;kmeans聚类、dbscan聚类、层次聚类3.三种算法对应的流程作业&#xff1a; 对心脏病数据集进行聚类。 DAY 17 常见聚类算法 import seaborn as sns from sklearn.decomposition import PCA from sklearn.prepro…

MySQL存储架构深度解析:从引擎选型到云原生实践(2025最新版)

引言 在数字经济时代&#xff0c;MySQL作为全球使用最广泛的关系型数据库&#xff0c;其存储技术直接影响着全球70%以上互联网企业的数据处理能力。2025年云原生数据库市场规模预计突破$50B&#xff0c;而MySQL存储引擎的选型与优化仍是DBA的核心课题。本文将结合最新行业实践…