当前位置: 首页 > news >正文

【JavaEE初阶】深入解析死锁的产生和避免以及内存不可见问题

前言:

🌈上期博客:【后端开发】JavaEE初阶—线程安全问题与加锁原理(超详解)-CSDN博客

🔥感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客

⭐️小编会在后端开发的学习中不断更新~~~

🥳非常感谢你的支持

 

目录

📚️1.引言

📚️2.可重入锁

2.1概念

 2.2原理理解

 📚️3.死锁

 3.1产生死锁的情况

1.一个线程,一把锁

2.两个线程,两把锁

 3.N个线程,M把锁

3.2解决死锁的方法

📚️4.内存可见性

4.1内存可见性实例

4.2内存可见性原理

 4.3内存可见性解决

1.进行线程休眠

2.添加volatile关键词 

📚️5.总结


📚️1.引言

      OK啊!!!小伙伴们,本小编又带来了一个重磅知识,我们上期讲解了关于线程安全问题,引出了加锁这个概念;但是加锁会产生一个严重的问题,就是当我们运用不当时,进行加锁会导致死锁的发生,那怎样才会导致死锁呢?以及如何避免呢?这就是小编本期的重要内容;

发车发车gogogog~~~🥳🥳🥳;

且听小编讲解,包你学会!!! 

📚️2.可重入锁

2.1概念

什么是可重入锁呢???,让我们看看以下代码:

 public static void main(String[] args) {Object lock=new Object();//可重入锁实例Thread t1=new Thread(()->{synchronized (lock){synchronized (lock){System.out.println("Hello thread");}}});t1.start();        }

对于如何进行加锁操作,小编上期有讲,不清楚的小伙伴可以自己去看看哦~~~

开始认知:这里由于lock已经被加过一次锁了,那么接下来再加一次锁,不会发生线程阻塞吗,第一次加又没有进行释放; 

注意:上面这种理解完全是错误的,这里就是由于使用同一个线程,此时的锁对象,就能够知道第二次加锁的线程,是持有锁的线程,那么在第二次加锁时,就直接通过,就不会发生“阻塞”现象

这种特性叫:可重入性,这个锁就叫做可重入锁~~~

 2.2原理理解

在Java中实现可重入锁是非常简单的,因为synchrinized自带这个特性,那么这个特性的内部原理是啥呢,且看如下图所示:

注意:对于重入锁来说,最主要两部分第一个就是对于加锁的线程是否为同一个线程,第二就是对于加锁过程的计数器的次数理解;

以上都是在java中synchronized封装实现的,那么在C++中就没有重入锁的概念,此时当存在复杂的调用关系的时候,就会存在卡死的情况,就是“死锁”,接下来就注重“死锁”的理解;

 📚️3.死锁

在之前讲解过,加锁可以解决线程安全问题,但是操作不当会产生“死锁”的情况;

 3.1产生死锁的情况

1.一个线程,一把锁

即在上述讲解过程中的可重入所情况,但是如果没有可重入这个性质,那么连续对一个线程加锁两次,那么就会产生死锁;

2.两个线程,两把锁

即有两个线程,当线程1加上锁A,线程2加上锁B,那么然后在两个锁不进行释放的前提下,双方都想拿到对方的锁,此时就会发生死锁的情况;

这里有代码进行演示:

Object A=new Object();//对象AObject B=new Object();//对象B//创建线程t1拿到锁Thread t1=new Thread(()->{synchronized (A) {System.out.println("线程t1拿到了锁A");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException();}//线程进入休眠,保证另一个线程也能够拿到锁//尝试拿到对方的锁,此时锁A没有释放synchronized (B) {System.out.println("线程t1拿到了两把锁");}}});

此时小编设置了两个对象,即两个锁对象,那么我们就进行线1的加锁,此时当我们拿到锁A后,在不解开锁的情况下进行另外一把锁B的获取:

Thread t2=new Thread(()->{synchronized (B) {System.out.println("线程t2拿到了锁B");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException();}//线程进入休眠,保证另一个线程也能够拿到锁//尝试拿到对方的锁,没有释放自己的锁synchronized (A) {System.out.println("线程t2拿到了两把锁");}}});

此时,我们就行第二个线程的实现,和上述线程1一样,当拿到自己的锁之后,在不解开锁的情况下进行锁A的获取,然后两个线程启动之后的结果就是:

此时可以发现,线程各自拿到自己的锁之后,就直接“卡住”了,这就发生了线程安全问题;

当我们打开jconsole后,可以看看我们的线程情况:

这是我们的两个对应的线程名字,此时两个线程的执行状态就如下图所示:

注意:此时可以看到线程1处于BLOCKED状态,并且在等锁B,那么锁B的拥有者是线程2;同理,线程2在等锁A,而锁A的拥有者就是线程1;

可以发现此时两个锁都在等对方释放锁,此时就产生了死锁;

 3.N个线程,M把锁

此时这种情况就是要考虑到“哲学家就餐问题了”,什么是哲学家就餐问题呢???

解释:此时有5个哲学家要吃面,但是筷子只有5根,这无根筷子在每个哲学家之间,此时就是五个哲学家就是五个线程筷子就是锁,当每个哲学家拿到筷子后,旁边的两哲学家是吃不到的,就处于阻塞的状态,一般情况下哲学家啥时候吃到面是一个随机问题,一般情况下这是没有问题的~~~

注意:当我们每个哲学家左手拿起筷子时,可以发现此时每个哲学家都吃不到面(吃面要两根筷子),都等待另一个哲学家释放筷子(锁),此时就发生了线程的阻塞 ;

3.2解决死锁的方法

在了解线程的解决死锁之前我们要知道产生死锁的必要条件

(重点)

1.互斥使用:当一个线程获取到锁之后,另一个线程也想要获取,那么此时就要进行阻塞

2.不可抢占:当一个线程获得锁之后,其他线程想要获取此时就要等到锁的释放,不能强行占用

3.请求保持:当一个线程获得锁A之后,尝试再次获取锁B(锁A是没有释放的)

4.循环等待/环路等待

以上就是死锁形成的必要条件,缺一不可~~~;

那么针对以上死锁的产生条件,第三个条件是根据具体的代码来进行实现的,但是我们可以根据最后一个来进行攻破;

注意:解决哲学家问题关键:针对五把锁我们可以对其进行编号,然后每个哲学家也进行编号,此时约定,每个哲学家开始只能够拿编号比自己小的锁,然后再拿比自己编号大的锁 

为啥能够解决死锁问题呢???且看下图所示:

过程解释:当我们为这个线程和锁进行编号后,此时由于只能拿比自己编号小的筷子,那么2号哲学家拿1筷子,3号哲学家拿2号筷子.......到最后,5号哲学家拿4号筷子,此时就可以发现多了一双筷子,那么5号哲学家先再拿起5号筷子,此时当5号哲学家吃完后,放下筷子,一次类推,可以保证每个哲学家都拿够吃到面~~~,同理这就解决了死锁这个问题;

那么此时我们就可以改变之前这个双方获取两个锁的这个代码,实现这个代码的可行性:

Thread t2=new Thread(()->{synchronized (A) {System.out.println("线程t2拿到了锁A");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException();}//线程进入休眠,保证另一个线程也能够拿到锁//尝试拿到对方的锁,没有释放自己的锁synchronized (B) {System.out.println("线程t2拿到了两把锁");}}});

注意:这里小编只改了第二个线程的获取锁的顺序,即可保证两个线程都能够拿到锁;

解析:这里能够执行原因:当两个线程启动的时候,线程1获取到了锁A,所以此时线程B规定先不获取锁,即他以获取锁A来发生阻塞,当线程1执行完后,线程2就能够得到两个线程了~~~,这就是引入了加锁顺序规则~~~

 总结:

解决死锁有很多办法,以下是一些小编总结的一些方法:

1.添加“筷子”;

2.去掉一个线程

3.引入计数器,规定最多同时几个人吃面

4.引入加锁顺序规则

5.“银行家算法”

1~3:虽然能够解决这个问题,但是普适性不高;

4:是小编推荐的,普适性高,而且容易实现;

5:是可以解决这个死锁问题,但是不推荐,实现过程很复杂,理论成立,现实不行;

📚️4.内存可见性

4.1内存可见性实例

内存可见性问题:即一个程序读,一个程序写的过程中产生的线程安全问题;

小编就用代码实例来演示:

public static void main(String[] args) {Thread t1=new Thread(()->{while (flag==0){//不输出任何;               }System.out.println("flag的值进行了改变");});Thread t2=new Thread(()->{System.out.println("输入一个flag的值");Scanner scanner=new Scanner(System.in);flag=scanner.nextInt();});//起启动线程t1.start();t2.start();}

解释:此时我们规定线程1进行读的操作,若flag是0,那么就不会打印任何日志,此时小编在线程2上进行改变,线程1中flag的值,让其不满足条件,实现跳出循环,结束线程;但是输出如下:

可以发现在小编输入1,改变flag的值之后,回车并没有输出“flag的值进行了改变”,所以此时就发生了线程安全问题,即内存可见性问题~~~

4.2内存可见性原理

这里从核心指令入手:

while (flag==0){//不输出任何;               
}

注意:这里的核心指令有两条

1.load读取内存当中的指令到寄存器中

2.寄存器拿着值与0进行比较

那么此时就是在线程2启动到输入这个操作,不断进行循环读取,比较的过程,所以这个操作有两个关键要点:

1.load不断从内存中读取数据到CPU寄存器上,这个操作的执行结果是一样的,几秒之内已经很多次了~~~

2.load从内存中读取数据这个操作开销远远大于寄存器比较这个操作~~~

此时就出现了一个问题:编译器优化代码这个操作,即JVM在优化中发现读取数据操作一直不变,那么优化后即将这个load读取数据操作给省去了(关键原因); 

代码优化:即JVM在保持原有代码逻辑不变的情况下,实现提高代码的效率,单线程还好,但是多线程很容易发生误判~~~

 4.3内存可见性解决

1.进行线程休眠

核心:在上述讲解中,是因为读取这个内存的次数过多,且没有改变,所以我们能够实现,读取次数减少的操作;

代码实现如下:

Thread t1=new Thread(()->{while (flag==0){//不输出任何;try {Thread.sleep(1000);}catch (InterruptedException e){e.printStackTrace();}}System.out.println("flag的值进行了改变");});

小编只需要在读取数据这个操作实现休眠即可;

注意:这里的休眠是为了减少从内存中读取数据到CPU寄存器上,让load开销减少,减少迫切优化的程度;此时JVM就不会进行优化了,那么就不会出现线程安全问题

2.添加volatile关键词 

volatile作用:这里的volatile关键词会阻止JVM对程序进行优化,确保每次循环都会从内存中读取数据到寄存器当中~~~

volatile核心作用:解决内存可见性问题,和禁止指令重排序~~~

代码演示:

public  volatile static int flag=0; //加上volatile实现代码优化的消除public static void main(String[] args) {Thread t1=new Thread(()->{while (flag==0){//不输出任何;                }System.out.println("flag的值进行了改变");});

注意:volatile和上述休眠作用基本一致,都是使JVM优化程序关闭,保证每次循环都是从内存中读取数据,而不是优化成直接从寄存器当中读取数据~~~

📚️5.总结

💬💬本期小编总结了关于多线程的重要知识即死锁,分别从造成原因和如何进解决提出了关于小编的理解,以及线程安全问题之内存可见性问题,并附上了代码供小伙伴们参考参考~~~

 🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!


💪💪💪以上就是本期内容了, 感兴趣的话,就关注小编吧。

                                                               😊😊  期待你的关注~~~

相关文章:

【JavaEE初阶】深入解析死锁的产生和避免以及内存不可见问题

前言: 🌈上期博客:【后端开发】JavaEE初阶—线程安全问题与加锁原理(超详解)-CSDN博客 🔥感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客 ⭐️小编会在后端开发的学习中不断更新~~~ &#…...

企微群管理软件:构建高效社群运营的新引擎

在数字化营销日益盛行的今天,企业微信(简称“企微”)群作为企业与用户直接互动的重要平台,其管理与运营效率直接关系到企业的品牌形象、用户满意度及市场影响力。企微群管理软件,作为专为企微社群设计的高效管理工具&a…...

CORE 中间件、wwwroot

ASP.NET Core中间件组件是被组装到应用程序管道中以处理HTTP请求和响应的软件组件(从技术上来说,组件只是C#类)。 ASP.NET Core应用程序中的每个中间件组件都执行以下任务。 选择是否将 HTTP 请求传递给管道中的下一个组件。这可…...

SpringBoot 与 Maven 快速上手指南

SpringBoot 与 Maven 快速上手指南 在Java开发领域,Spring Boot和Maven是两个极其重要的工具,它们极大地简化了企业级应用的开发和构建过程。Spring Boot通过自动配置和起步依赖等特性,让开发者能够快速搭建起一个Spring应用;而M…...

大觅网之自动化部署(Automated Deployment of Da Mi Network)

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…...

【C++】入门基础知识-1

🍬个人主页:Yanni.— 🌈数据结构:Data Structure.​​​​​​ 🎂C语言笔记:C Language Notes 🏀OJ题分享: Topic Sharing 目录 前言: C关键字 命名空间 命名空间介…...

Redis一些简单通用命令认识常用数据类型和编码方式认识Redis单线程模型

通用命令 get() / set() 这是Redis中两个最为核心的命令。 set插入 这里的key 和 value都是字符串,我们可以加双引号 或者单引号,或者不加。 get查找 如果查询的key值不存在,那么会返回一个 nil ,也就是代表空 在Redis中命令…...

使用电子模拟器 Wokwi 运行 ESP32 示例(Arduino IDE、VSCode、ESP32C3)

文章目录 Wokwi 简介安装客户端(Mac/Linux)创建 Token Arduino IDEVSCode 配置安装 wokwi 插件打开编译后目录 ESP32C3 示例Arduino IDE创建模拟器运行模拟器 Wokwi 简介 Wokwi 是一款在线电子模拟器。您可以使用它来模拟 Arduino、ESP32、STM32 以及许…...

C嘎嘎入门篇:类和对象(1)

前言: 小编在之前讲述了C的部分入门基础,读者朋友一定要掌握好那些,因为C的学习和C有点不同,C的知识都是比较连贯的,所以我们学好了前面才可以学习后面的内容,本篇文章小编将会讲述C真正的入门篇&#xff1…...

tomcat服务搭建部署ujcms网站

tomcat服务搭建部署ujcms网站 关闭selinux和防火墙 setenforce 0 && systemctl stop firewalld安装java环境 #卸载原有java8环境 yum remove java*#上传java软件包,并解压缩 tar -xf openjdk-11.0.1_linux-x64_bin.tar.gz && mv jdk-11.0.1 jdk11…...

unity_Occlusion_Culling遮挡剔除学习

unity_Occlusion_Culling遮挡剔除学习 文档: https://docs.unity.cn/cn/2019.4/Manual/occlusion-culling-getting-started.html没彻底搞明白,但是会用,虽然也不熟练 设置遮挡剔除 打开遮挡剔除面板 设置场景物体。设置为静态 设置场景 烘…...

vue初学随笔

Vue基础 Vue基本概念 Vue是什么 Vue是一个渐进式的JavaScript框架,它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。 渐进式:各个特性可以根据项目需要逐渐引入和…...

IDEA Dependency Analyzer 分析 maven 项目包的依赖

一、场景分析 javax.validation 是我们 SpringMVC 常用的数据校验框架。但是 javax.validation 是一个规范(Java Bean Validation,简称 JSR 380),它并没有具体的实现,它的常用实现,是hibernate-validator。…...

微信小程序 - 最新详细安装使用 Vant weapp UI 框架环境搭建详细教程

前言 自从 2024 年开始,小程序做了很多改变和升级, 导致网上很多搭建教程文章的教程失效了,本文来做最新的教程。 第一步 为了更贴合新手,我这里创建了一个纯净无任何业务代码的小程序项目。...

【C语言】手把手带你拿捏指针(完)(指针笔试、面试题解析)

文章目录 一、sizeof和strlen的对⽐1.sizeof2.strlen3.sizeof与strlen对比 二、数组和指针笔试解析1.一维数组2.字符、字符串数组和字符指针代码1代码2代码3代码4代码5代码6 3.二维数组4.总结 三、指针运算笔试题解析代码1代码2代码3代码4代码5代码6 一、sizeof和strlen的对⽐ …...

Vue中input框自动聚焦

在Vue中input自动聚焦的思路&#xff1a; 给需要聚焦的input设置ref <el-inputv-model"loginForm.username"ref"userNameInput"name"username"type"text"auto-complete"on"placeholder"username"keyup.enter.…...

基于Node.js+Express+MySQL+VUE实现的计算机毕业设计旅游推荐网站

猜你喜欢评论 登录注册搜索 推荐定制景点/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序 功能图如下所示&#xff1a; 一、设计目标 本次计算机毕业设计项目的主要目标是设计和开发一款功能完善、用户友好的旅游推荐网站。该网站旨在为广大旅游爱好者提供一个便捷、…...

已存在的Python项目使用依赖管理工具UV

1. 文档 uv文档 2. 如何转换 初始化 uv initrequirements.txt转换成pyproject.toml uv add $(cat requirements.txt)删除requirements.txt 如果更新pyproject.toml之后&#xff0c;使用命令 uv sync替换项目环境 如果有库没有加入依赖&#xff0c;自己手动加一下&am…...

JavaWeb美食推荐管理系统

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 spring-mybatis.xml3.5 spring-mvc.xml3.5 login.jsp 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优…...

如何像专家一样修复任何 iPhone 上的“iPhone 已禁用”错误

“我忘记了密码&#xff0c;并且我的 iPhone 在多次输入错误密码后就被禁用了&#xff0c;如何再次访问我的手机&#xff1f;” 作为最安全的数字设备之一&#xff0c;iPhone 必须使用正确的密码解锁。即使您可以使用 Face ID 或 Touch ID 访问您的设备&#xff0c;在充电或重…...

IPD实战指南:CBB模块化设计如何加速产品创新与资源整合

1. CBB模块化设计的本质与价值 第一次接触CBB这个概念时&#xff0c;我正负责一款智能家居产品的研发。当时团队为了赶进度&#xff0c;每个新产品都从零开始设计电路板&#xff0c;结果发现80%的功能模块都是重复的。这种低效的开发方式让我开始思考&#xff1a;能不能像搭积木…...

探索分子世界的三维画笔:PyMOL开源版如何让你成为分子艺术家?

探索分子世界的三维画笔&#xff1a;PyMOL开源版如何让你成为分子艺术家&#xff1f; 【免费下载链接】pymol-open-source Open-source foundation of the user-sponsored PyMOL molecular visualization system. 项目地址: https://gitcode.com/gh_mirrors/py/pymol-open-so…...

OpCore Simplify:终极指南!让黑苹果配置从8小时缩短到45分钟的自动化神器

OpCore Simplify&#xff1a;终极指南&#xff01;让黑苹果配置从8小时缩短到45分钟的自动化神器 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 还在…...

TikTok零/低播放突围:跨境账号实战破局指南

图片来源&#xff1a;TK云大师0播放或低播放是TikTok跨境从业者的高频痛点——行业数据显示&#xff0c;超68%新手账号遇初始零播放&#xff0c;45%带货账号因持续低播放停摆。耗时制作的内容无人问津&#xff0c;既耗资源又乱节奏。结合实操经验&#xff0c;本文从排查、挽救、…...

服务器 网络科技运行

服务器是网络科技运行的核心支撑&#xff0c;承担着数据存储、处理、应用部署及资源调度等关键职能&#xff0c;在网络科技领域&#xff0c;服务器的稳定运行直接关系到整个业务系统的顺畅与否&#xff0c;无论是企业内部的办公系统、数据管理平台&#xff0c;还是面向公众的互…...

2026最权威一键生成论文工具榜单:这些被高校和导师悄悄推荐的软件你用了吗

一键生成论文工具正成为学术研究的重要助力&#xff0c;其高效性与专业性在近年来得到广泛认可。依托权威检测平台数据、高校实测反馈及用户真实评价&#xff0c;这些工具已逐步成为科研工作者和学生群体的得力助手。本文将盘点2026年最受高校和导师推荐的一键生成论文软件&…...

《其他 W3C 活动》

《其他 W3C 活动》 引言 W3C&#xff08;World Wide Web Consortium&#xff0c;万维网联盟&#xff09;是全球领先的互联网技术标准制定机构。自1994年成立以来&#xff0c;W3C致力于推动互联网技术的标准化&#xff0c;为全球的互联网发展做出了重要贡献。除了核心的HTML、CS…...

FastAdmin+PHPStudy保姆级安装教程:从下载到配置数据库的完整流程

FastAdminPHPStudy极速开发环境搭建实战指南 作为一名长期使用FastAdmin框架的开发者&#xff0c;我深知一个顺畅的本地开发环境对项目效率的影响。本文将带你从零开始&#xff0c;用最简洁的方式完成FastAdmin与PHPStudy的完美搭配&#xff0c;避开那些新手常踩的"坑&quo…...

nli-distilroberta-base生产环境:金融风控中合同条款中立性识别实践

nli-distilroberta-base生产环境&#xff1a;金融风控中合同条款中立性识别实践 1. 项目背景与价值 在金融风控领域&#xff0c;合同条款的准确理解至关重要。传统人工审核方式效率低下且容易遗漏关键细节&#xff0c;而自然语言理解技术可以大幅提升审核效率和准确性。nli-d…...

目标检测实战:从VOC XML到YOLO格式的自动化数据流水线

1. 为什么需要VOC转YOLO格式 在目标检测任务中&#xff0c;数据格式的统一性直接影响着模型训练的效率。VOC&#xff08;PASCAL VOC&#xff09;和YOLO是两种最常见的标注格式&#xff0c;但它们的存储方式截然不同。VOC采用XML文件记录目标的类别和边界框坐标&#xff0c;而YO…...