多线程的死锁问题

fly

可重入和不可重入😊😊😊

一个线程针对同一个对象,连续加锁两次,是否会有问题 ~~ 如果没问题,就叫可重入的.如果有问题,就叫不可重入的.

代码示例🍉🍉🍉:

synchronized public void add(){synchronized (this){count++;}
}

解析😍😍😍:
锁对象是this,只要有线程调用add,进入add方法的时候,就会先加锁(能够加锁成功).紧接着又遇到了代码块,再次尝试加锁.站在this 的视角(锁对象),它认为自己已经被另外的线程给占用了,这里的第二次加锁是否要阻塞等待呢?🤔🤔🤔
如果第二次加锁成功,这个锁就是可重入的,Java中的synchronized 是“可重入锁”.
如果第二次加锁会阻塞等待,就是不可重入的,这样的锁称为“不可重入锁”.

这时新的情况就出现了😕😕😕:

场景如下: 一个线程没有释放锁, 然后又尝试再次加锁,这个锁是“不可重入锁”,第二次加锁的时候, 就会阻塞等待,直到第一次的锁被释放, 才能获取到第二个锁,但是释放第一个锁也是由该线程来完成, 结果这个线程已经躺平了, 啥都不想干了, 也就无法进行解锁操作,这时候就会死锁.
注: 博主后面的内容会重点讲解死锁.

Java 标准库中的线程安全类🍭🍭🍭

多个线程操作同一个集合类,就需要考虑到线程安全的事情.
1.Java 标准库中很多集合类都是线程不安全的,没有任何加锁措施.
比如:ArrayList,LinkedList,HashMap,TreeMap,HashSet,TreeSet,StringBuider.
2.但是还是有一些内置了synchronized加锁的集合类,线程是相对来说,更安全一点的,
比如: Vector(不推荐使用),HashTable(不推荐使用),StringBuffer,ConcurrentHashMap.
3.最后就是String它没有加锁,但是由于是不可变对象,不涉及修改,线程是绝对安全的

问题: 既然加锁了,线程会变得安全一些,为什么集合类都不加上锁了🤔🤔🤔🤔?
原因: (1).加锁这个是有副作用的,会产生额外的时间开销.
(2).对程序猿来说,有更多的选择空间,这些没内置加锁机制的集合类,当没有线程安全问题的时候,就可以放心用,当有线程安全问题的时候,可以手动加锁.但是像StringBuffer这些内置synchronized加锁的结合类,就没有这种选择,这也是为什么是Vector,HashTable不推荐使用的原因之一.

死锁❤️❤️❤️

~~ 死锁是一个非常影响我们幸福感问题.
一旦程序出现死锁,就会导致线程就跪了,无法继续执行后续工作了,程序势必会有严重bug,在我们写代码的时候,不经意间,就会写出死锁代码,并且这玩意还不容易测试出来.

死锁的三个典型情况🍁🍁🍁

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

~~ 如果锁是“不可重入锁”,就会死锁.
注: Java里 synchronized 和 ReentrantLock 都是“可重入锁”.C++,Python,操作系统原生的加锁API都是不可重入的.

2.两个线程两把锁🍎🍎🍎

~~ 线程 t1 和线程 t2 各自先针对锁A和锁B加锁,再尝试获取对方的锁.
例子: (1)可以理解为你把家里门的钥匙锁在车里了,而车钥匙锁在家里了,最后,你不仅车开不了呢,家也回不去了
(2)有一个东北人和一个陕西人正坐在饺子馆的同一个餐桌上准备吃饺子,东北人吃饺子,喜欢蘸酱油,但他面前只有一瓶醋;陕西人吃饺子,喜欢蘸醋,但不巧的是他面前只有酱油;东北人说: “兄弟,你把酱油给我,我用完了之后给你醋”,但是陕西人也说:”老兄,你把醋给我,我用完了之后给你酱油”.假设哈,注意这是假设(现实生活一般不可能会这样)!!!如果这两人互不相让,此时就僵持住了.
例子2的代码:

public class ThreadDemo14 {public static void main(String[] args) {Object jiangyou = new Object();// jiangyou => 酱油Object cu = new Object();// cu => 醋Thread dongbeiren = new Thread(() -> {// dongbeiren => 东北人synchronized (cu) {try {Thread.sleep(1000);// 确保两个线程都先把第一个锁拿到 => 线程是抢占式执行的} catch (InterruptedException e) {e.printStackTrace();}synchronized (jiangyou) {System.out.println("东北人把醋和酱油都拿到了");}}});Thread shanxiren = new Thread(() -> {// shanxiren => 陕西人synchronized (jiangyou) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (cu) {System.out.println("陕西人把酱油和醋都拿到了");}}});dongbeiren.start();shanxiren.start();}
}

运行结果:

image-20230926104443616

使用 jconsole 查看线程的情况

image-20230926112333971

注: 针对死锁问题,是需要借助像 jconsole 这样的工具进行定位,看线程的状态和调用栈,从而分析出代码是在哪里死锁了.
image-20230926113122179

image-20230926113420416

3.多个线程,多把锁🍀🍀🍀

哲学家就餐问题 ~~ 教科书上的经典案例
image-20230926124320039

想要解决死锁问题,就得分析一下死锁的形成.

死锁的四个必要条件(缺一不可)🌸🌸🌸:

1.互斥使用: 一个线程拿到一把锁之后,另一个线程就不能使用了(锁的基本特性).
2.不可抢占: 一个线程拿到锁之后,必须是这个线程主动释放,其它线程就获取不到了(墙角是挖不了滴).
3.请求和保持: 线程1拿到锁A之后,再去尝试获取锁B,这时线程1的A这把锁还是保持的,不会因为获取锁B就把锁A给释放了.
个人理解“吃着碗里的,惦记锅里的”,一个男生有了女朋友后,还去和其他女生搞暧昧,追求其他女生(渣男).
4.循环等待: 线程1尝试获取到锁A和锁B,线程2尝试获取到锁B和锁A,线程1在获取B的时候等待线程2的释放;同时,线程2在获取A的时候等待线程1释放锁A(或者可以理解家钥匙锁车里了,车钥匙锁家里了).

注: 条件1,2,3都是锁的基本特性,对于,synchronized这把锁来说,是无法改对.循环等待是这四个条件中唯一一个和代码结构相关的,也是作为程序猿的我们可以控制的.

解决死锁的方法其实很简单,就是破解循环等待这个必要条件🍃🍃🍃🍃:
针对锁进行编号,在需要同时获取多把锁的时候,约定加锁顺序,务必先对小的编号加锁,后对大的编号加锁(这是解决死锁,最简单可靠的办法).
注: 解决死锁,还有个银行家算法,本质上是对资源的更合理的分配,比较复杂,不适合在实际开发中使用,但是是学校操作系统课的期末考试必考题,建议上这门课的时候认真听一下,博主不在这讲解,因为我也不太理解,讲不来,(●’◡’●).
代码:

public class ThreadDemo14 {public static void main(String[] args) {// 假设 jiangyou 是 1 号, cu 是 2 号, 约定先拿小的, 后拿大的Object jiangyou = new Object();// jiangyou => 酱油Object cu = new Object();// cu => 醋Thread dongbeiren = new Thread(() -> {// dongbeiren => 东北人synchronized (cu) {try {Thread.sleep(1000);// 确保两个线程都先把第一个锁拿到 => 线程是抢占式执行的} catch (InterruptedException e) {e.printStackTrace();}synchronized (jiangyou) {System.out.println("东北人把醋和酱油都拿到了");}}});Thread shanxiren = new Thread(() -> {// shanxiren => 陕西人synchronized (cu) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (jiangyou) {System.out.println("陕西人把酱油和醋都拿到了");}}});dongbeiren.start();shanxiren.start();}
}

运行结果:

image-20230926143143122

多线程当前的知识点梳理🎀🎀🎀

  1. 线程的基本概念.轻量,共享资源,节省了资申请的开销

  2. Thread类的使用

    1. 创建
      1. 继承Thread,重写run
      2. 实现Runnable,重写run
      3. 匿名内部类,继承Thread,重写run
      4. 匿名内部类,继承实现Runnable,重写run
      5. lambda表达式
    2. 终止
      1. 自己定义一个标志位
      2. 使用线程内置的标志位
        1. isInterruptted(),判断标志位是否为true
        2. interrupt(),设置标志位为true,还能以异常的方式唤醒sleep
      3. 其它线程只能通知该线程要终止了,这个线程是否真的终止,是它自己的事情~~
    3. 等待
      1. join ~~ 在线程a中,调用b.join(),此时就是线程a等待线程b结束.
      2. 阻塞
    4. 获取线程引用 ~~ Thread.currentThread();
    5. 休眠 ~~ sleep
  3. 线程状态

    1. NEW ~~ 初创状况
    2. TERMINATED ~~ 消亡状况
    3. RUNNABLE ~~ 就绪状况
    4. TIMED_WAITING ~~ 等待一段时间
    5. WAITING
    6. BLOCKED
  4. 线程安全

    一个多线程实际的执行顺序有多种变数 ~~ 线程安全就是在所有的变数下,都能够运行正确!!!

    1. 抢占式执行,随机调度

    2. 多个线程同时修改同一个变量

    3. 修改操作不是原子性的 ~~ 加锁(synchronized)

    4. 内存可见性问题

    5. 指令重拍序

  • 加锁 ~~ synchronized

关于加锁

  1. 修饰方法

    • 普通方法 ~~ 把锁加到this对象上
    • 静态方法 ~~ 把锁加到类对象上
  2. 修饰代码块 ~~ 手动指定加到那个对象上

  3. 锁对象

    1. 两个线程,针对同一个对象加锁,会发生锁冲突/锁竞争(产生阻塞等待)
    2. 两个线程,针对不同的对象加锁,不会有任何锁冲突
  4. 死锁

    1. 死锁的概念

    2. 死锁的三个典型情况

      1. 一个现场一把锁.连续加锁两次

      2. 两个线程两把锁,分别获取对方的锁

      3. N个线程,M把锁

    3. 可重入和不可重入

      • 线程针对同一个对象,连续加锁二次,是否会死锁

      • 会死锁,就叫可重入,不会死锁,就叫不可重入的.

      • 注: synchronized 是可重入的

    4. 死锁的四个必要条件

      • 最核心的就是“循环等待”

      • 解决: 在针对多把锁加锁的时候,约定好锁的顺序

    5. 如何破除死锁

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

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

相关文章

大模型助力企业数据驱动,火山引擎数智平台发布AI助手

9月19日,火山引擎在其举办的“V-Tech数据驱动科技峰会”上宣布,火山引擎数智平台VeDI推出“AI助手”,通过接入人工智能大模型,帮助企业提升数据处理和查询分析的效率。即使是不会写代码的运营人员,和大模型对话也能做好…

【CNN-FPGA开源项目解析】卷积层03--单格乘加运算单元PE 单窗口卷积块CU 模块

03–单格乘加运算单元PE & 单窗口卷积块CU 文章目录 03--单格乘加运算单元PE & 单窗口卷积块CU前言单格乘加运算单元PE代码模块结构时序逻辑分析对其上层模块CU的要求 单窗口卷积块CU代码逻辑分析 前言 ​ 第一和第二篇日志已经详细阐述了"半精度浮点数"的加…

贪心算法-

代码随想录 什么是贪心 贪心的本质是选择每一阶段的局部最优,从而达到全局最优。 这么说有点抽象,来举一个例子: 例如,有一堆钞票,你可以拿走十张,如果想达到最大的金额,你要怎么拿&#xff…

通过 HelpLook ChatBot AI自动问答机器人降低客户服务成本

在当今竞争激烈的商业环境中,提供卓越的客户服务对于维持忠诚的客户群和推动业务增长至关重要。客户服务涵盖了公司与其客户之间的所有互动,包括解答问题、解决问题和提供支持。它在塑造客户对品牌的看法方面起着关键作用,并且可以显著影响他…

如何通过bat批处理实现快速生成文件目录,一键生成文件名和文件夹名目录

碰对了情人,相思一辈子。 具体方法步骤: 一、创建一个执行bat文件(使用记事本即可); 1、新建一个txt文本空白记事本文件 2、复制以下内容进记事本内 dir/a/s/b>LIST.TXT (其中LIST.TXT文件名是提取后将…

【微服务】spring 控制bean加载顺序使用详解

目录 一、前言 二、使用order注解控制顺序 2.1 order 注解使用示例 2.2 order注解顺序失效问题 2.2.1 order失效问题解决办法 2.3 实现Ordered接口 三、使用dependon注解控制顺序 四、AutoConfiguration注解控制bean加载顺序 4.1 AutoConfigureBefore 操作演示 4.2 A…

安防视频平台EasyCVR视频调阅全屏播放显示异常是什么原因?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

AIRIOT亮相IOTE2023深圳物联网展,产品创新力再获“IOTE金奖”

9月20-22日,IOTE 2023第二十届深圳国际物联网展在深圳国际会展中心(宝安)圆满落幕。作为物联网领域年度最重要的行业盛会之一,本届展会以“IoT构建数字经济底座”为主题,汇聚全球来自工业、物流、基建、智慧城市、智慧…

freemarker自定义模板

模板编程器指南 <dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.31</version> </dependency>freemarker官网参考&#xff1a; https://freemarker.apache.org/docs/pgui_qu…

浏览器原生JavaScript离线文字转语音TTS播放,支持Windows自带TTS语音和移动端(安卓、IOS)

前言 JS已经可以实现语音合成(文字转语音)和语音识别(语音转文字),各个浏览器支持列表如下所示: 语音识别支持列表: 因此,浏览器上面使用语音合成非常简单。 页面效果示例: 实现功能 1、支持速度,音调设置 2、支持下拉选择语音模板 3、文字转语音 代码实现 …

云服务器 CentOS7 操作系统上安装Jpress (Tomcat 部署项目)

1、xShell 和 xftp 下载安装&#xff08;略&#xff09; https://www.xshell.com/zh/free-for-home-school/2、xftp 连接云服务器 xftp 新建连接 3、JDK 压缩包下载 下载 jdk1.8 注&#xff1a;此处 CentOS7 是64位&#xff0c;所以下载的是&#xff1a;Linux x64&#xf…

Hbuilder本地调试微信H5项目(二)--添加UView框架插件

摘要 在一个已创建的Hbuilder项目中&#xff0c;添加uView框架插件 前置准备 已安装Hbuilder 已创建uni-app的H5默认模板项目 实现逻辑 在Hbuilder官网找到组件说明页面 下载插件并导入HbuilderX 具体实现 访问网站 访问网址Hbuilder的uView1.8.6版本说明页 或者访问…

Python3操作MySQL8.XX创建表|CRUD基本操作

Python3操作MySQL8.XX创建表|CRUD基本操作 Python3操作SQLite3创建表主键自增长|CRUD基本操作 一&#xff1a; Python3操作Mysql数据库建表 import pymysqlPython3操作Mysql创建表&#xff1a; # 打开数据库连接 db pymysql.connect(host"localhost", user"您…

芯片SoC设计你了解吗?

数字IC设计根据岗位性质一般包含SOC设计&#xff0c;前端设计&#xff0c;ASIC设计&#xff0c;逻辑设计&#xff0c;IP设计&#xff0c;CPU设计等。 有人说&#xff1a;做IP设计就是翻译官&#xff0c;做SOC设计就是连连看。 SoC设计是做什么的&#xff1f;与IP设计有什么不同…

现代架构设计:构建可伸缩、高性能的分布式系统

文章目录 第1节&#xff1a;引言第2节&#xff1a;架构设计的关键原则2.1 微服务架构2.2 异步通信2.3 数据分区和复制2.4 负载均衡 第3节&#xff1a;代码示例3.1 创建产品服务3.2 创建消息队列3.3 创建产品更新服务 第4节&#xff1a;性能优化和监控4.1 建立性能基准4.2 水平扩…

解密智能化评估在培训考试系统中的应用

智能化评估在培训考试系统中的应用旨在提供更全面和准确的评估方式&#xff0c;以帮助培训机构或个人评估学员的学习成果。该系统结合了现代技术和评估理论&#xff0c;能够自动化地进行评估、反馈和分析&#xff0c;提供个性化的学习支持和指导。 智能化评估系统通过采集学员…

TensorFlow入门(四、数据流向机制)

session与"图"工作过程中存在的两种数据的流向机制,即:注入机制和取回机制 注入机制(feed):即通过占位符向模式中传入数据 取回机制(fetch):指在执行计算图时&#xff0c;可以同时获取多个操作节点的计算结果 实例代码如下: import tensorflow.compat.v1 as tftf…

信息收集进阶版-张榜公告型收集

信息收集进阶版-张榜公告型收集 一、思路&#xff08;1&#xff09;张榜公告型收集1.明确思维&#xff0c;构建思维导图2.逐行分析①利用FOFA、SHODAN、Hunter来直接精确定位到想要的资产②调用nmap来确认端口是否是正常开放③批量检测收集到的资产是否是正常回复④编写POC检测…

csdn未经允许将我的文章设置成vip收费

以前在csdn写了一些笔记&#xff0c;后来不用csdn了&#xff0c;想着留下这些笔记或多或少能帮助其他初学者&#xff0c;就没管它。结果csdn把文章设置成收费了&#xff0c;这个收费不是我本人弄的&#xff0c;是csdn弄的&#xff01;我现在只能把这些文章删除掉了。

【慕伏白教程】 Linux 深度学习服务器配置指北

文章目录 镜像烧录系统安装系统配置常用包安装 镜像烧录 下载 Ubuntu 镜像 Ubuntu 桌面版 下载烧录工具 balenaEtcher 准备至少 8G 的 空白U盘 开始烧录 系统安装 开机进入BIOS&#xff0c;修改U盘为第一启动 选择 Try or Install Ubuntu 往下拉&#xff0c;选择 中文&a…