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

文章目录

  • 关于死锁
    • 一.死锁的三种情况
      • 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.pswp.cn/pingmian/83035.shtml

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

相关文章

【NLP基础知识系列课程-Tokenizer的前世今生第二课】NLP 中的 Tokenizer 技术发展史

从词表到子词&#xff1a;Tokenizer 的“进化树” 我们常说“语言模型是理解人类语言的工具”&#xff0c;但事实上&#xff0c;模型能不能“理解”&#xff0c;关键要看它接收到了什么样的输入。而 Tokenizer&#xff0c;就是这一输入阶段的设计者。 在 NLP 的发展历程中&am…

Rust 学习笔记:循环和迭代器的性能比较

Rust 学习笔记&#xff1a;循环和迭代器的性能比较 Rust 学习笔记&#xff1a;循环和迭代器的性能比较示例 1示例 2总结 Rust 学习笔记&#xff1a;循环和迭代器的性能比较 示例 1 我们运行一个基准测试&#xff0c;将《福尔摩斯探案集》的全部内容加载到一个字符串中&#x…

pod创建和控制

一、引言 ‌主题‌&#xff1a;pod以及控制器模式中的Deployment作用。‌控制器模式&#xff1a;使用一种API对象&#xff08;如Deployment&#xff09;管理另一种API对象&#xff08;如Pod&#xff09;的方式。 二、容器镜像与配置文件 ‌容器镜像‌&#xff1a;应用开发者…

HTML实战:爱心图的实现

设计思路 使用纯CSS创建多种风格的爱心 添加平滑的动画效果 实现交互式爱心生成器 响应式设计适应不同设备 优雅的UI布局和色彩方案 <!DOCTYPE html> <html lang"zh-CN"> <head> <meta charset"UTF-8"> <meta nam…

2022年 中国商务年鉴(excel电子表格版)

2022年 中国商务年鉴&#xff08;excel电子表格版&#xff09;.ziphttps://download.csdn.net/download/2401_84585615/89772883 https://download.csdn.net/download/2401_84585615/89772883 《中国商务年鉴2022》是由商务部国际贸易经济合作研究院主办的年度统计资料&#xf…

Redis核心数据结构操作指南:字符串、哈希、列表详解

注&#xff1a;此为苍穹外卖学习笔记 Redis作为高性能的键值数据库&#xff0c;其核心价值来自于丰富的数据结构支持。本文将深入解析字符串&#xff08;String&#xff09;、哈希&#xff08;Hash&#xff09;、**列表&#xff08;List&#xff09;**三大基础结构的操作命令&…

如何以 9 种方式将照片从 iPhone 传输到笔记本电脑

您的 iPhone 可能充满了以照片和视频形式捕捉的珍贵回忆。无论您是想备份它们、在更大的屏幕上编辑它们&#xff0c;还是只是释放设备上的空间&#xff0c;您都需要将照片从 iPhone 传输到笔记本电脑。幸运的是&#xff0c;有 9 种方便的方法可供使用&#xff0c;同时满足 Wind…

如何使用Python从MySQL数据库导出表结构到Word文档

在开发和维护数据库的过程中&#xff0c;能够快速且准确地获取表结构信息是至关重要的。本文将向您展示一种简单而有效的方法&#xff0c;利用Python脚本从MySQL数据库中提取指定表的结构信息&#xff0c;并将其导出为格式化的Word文档。此方法不仅提高了工作效率&#xff0c;还…

写作-- 复合句练习

文章目录 练习 11. 家庭的支持和老师的指导对学生的学术成功有积极影响。2. 缺乏准备和未能适应通常会导致在挑战性情境中的糟糕表现。3. 吃垃圾食品和忽视锻炼可能导致严重的健康问题,因此人们应注重保持均衡的生活方式。4. 昨天的大雨导致街道洪水泛滥,因此居民们迁往高地以…

QT使用说明

QT环境准备 推荐Ubuntu平台上使用&#xff0c;配置简单&#xff0c;坑少。 Ubuntu 20.04 安装 sudo apt-get install qt5-default -y sudo apt-get install qtcreator -y sudo apt-get install -y libclang-common-8-dev启动 qtcreatorHelloWorld 打开 Qt Creator。选择 …

React 第四十九节 Router中useNavigation的具体使用详解及注意事项

前言 useNavigation 是 React Router 中一个强大的钩子&#xff0c;用于获取当前页面导航的状态信息。 它可以帮助开发者根据导航状态优化用户体验&#xff0c;如显示加载指示器、防止重复提交等。 一、useNavigation核心用途 检测导航状态&#xff1a;判断当前是否正在进行…

列表单独展开收起同时关闭其余子项的问题优化

如图所示&#xff0c;当在列表中&#xff0c;需要分别单独点开子选项时&#xff0c;直接这样用一个index参数判断即可&#xff0c;非常简单方便&#xff0c;只需要满足点开当前index,然后想同index用null值自动关闭即可

WPF【11_5】WPF实战-重构与美化(MVVM 实战)

11-10 【重构】创建视图模型&#xff0c;显示客户列表 正式进入 MVVM 架构的代码实战。在之前的课程中&#xff0c; Model 和 View 这部分的代码重构实际上已经完成了。 Model 就是在 Models 文件夹中看到的两个文件&#xff0c; Customer 和 Appointment。 而 View 则是所有与…

LangChain-结合魔塔社区modelscope的embeddings实现搜索

首先要安装modelscope pip install modelscope 安装完成后测试 from langchain_community.embeddings import ModelScopeEmbeddingsembeddings ModelScopeEmbeddings(model_id"iic/nlp_gte_sentence-embedding_chinese-base")text "这是一个测试句子"…

可定制化货代管理系统,适应不同业务模式需求!

在全球化贸易的浪潮下&#xff0c;货运代理行业扮演着至关重要的角色。然而&#xff0c;随着市场竞争的日益激烈&#xff0c;货代企业面临着越来越多的挑战&#xff1a;客户需求多样化、业务流程复杂化、运营成本上升、利润空间压缩……这些挑战迫使货代企业不断寻求创新和突破…

Lyra学习笔记2 GFA_AddComponents与ULyraPlayerSpawningManagerComponent

目录 前言GameFeatureAction_AddComponentsULyraPlayerSpawningManagerComponent缓存所有PlayerStart位置选择位置 前言 1.以control模式为例 2.比较散&#xff0c;想单独拿出一篇梳理下Experience的流程 GameFeatureAction_AddComponents 这部分建议看 《InsideUE5》GameFeatu…

进程生命周期

进程生命周期 Linux是多任务操作系统&#xff0c;系统中的每个进程能够分时复用CPU时间片&#xff0c;通过有效的进程调度策略实现多任务并行执行。进程在被CPU调度运行&#xff0c;等待CPU资源分配以及等待外部事件时会处于不同的状态。进程状态如下&#xff1a; 创建状态&a…

文字转图片的字符画生成工具

软件介绍 今天要介绍的这款软件可以将文字转换成图片的排列形式&#xff0c;非常适合需要将文字图形化的场景&#xff0c;建议有需要的朋友收藏。 软件名称与用途 这款软件名为《字符画大师》&#xff0c;是一款在网吧等场所非常流行的聊天辅助工具&#xff0c;其主要功能就…

历年南京大学计算机保研上机真题

2025南京大学计算机保研上机真题 2024南京大学计算机保研上机真题 2023南京大学计算机保研上机真题 在线测评链接&#xff1a;https://pgcode.cn/school Count Number of Binary Strings 题目描述 Given a positive integer n n n ( 3 ≤ n ≤ 90 3 \leq n \leq 90 3≤n≤…

王树森推荐系统公开课 排序06:粗排模型

shared bottom 表示神经网络被所有特征共享。精排模型主要开销在神经网络&#xff0c;神经网络很大且很复杂。 每做一次推荐&#xff0c;用户塔只做一次推理。物品塔存放入向量数据库。 后期融合模型常用于召回&#xff0c;前期融合模型常用于精排。 物品塔短时间内比较稳…