常见锁策略之可重入锁VS不可重入锁

news2025/1/12 6:45:23

可重入锁VS不可重入锁

有一个线程,针对同一把锁,连续加锁两次,如果产生了死锁,那就是不可重入锁,如果没有产生死锁,那就是可重入锁.

死锁

我们之前引入多线程的时候不是讲了一个加数字的案例么,我们今天以它来举例

当我们这样写的时候会出现什么问题?

分析:第一个synchronized的加锁对象是 this ,当我们继续执行代码,就会发现第二个synchronized的加锁对象还是 this ,此时就需要注意,当一个对象已经被加锁了,此时尝试对这个已经加锁的对象再一次的进行加锁,就会出现"锁竞争",我们要想使得第二个synchronized实现对 this 的加锁,就要让increase执行完毕,但是要想让increase执行完毕,就需要第二个synchronized加锁成功,此时就陷入了循环,就出现了矛盾.此时这个代码就卡在这里了,因此这个线程就僵住了.这是死锁的第一个体现形式.

这里的关键在于,两次加锁,都是"同一个线程",第二次尝试加锁的时候,该线程已经有了这个锁的权限了,这个时候,不应该加锁失败的,不应该阻塞等待的.

如果是一个不可重入锁,这把锁不会保存,是哪个线程对它加的锁,只要它当前处于加锁状态之后,收到了"加锁"这样的请求,就会拒绝当前加锁,而不管当下的线程是哪个,就会产生死锁.

如果是一个可重入锁,则是会让这个锁保存,是哪个线程加上的锁,后续收到加锁请求之后,就会先对比一下,看看加锁的线程是不是当前自己持有这把锁的线程,这个时候就可以灵活判定了.

但庆幸的是,synchronized本身就是一个可重入锁,实际上我们上述的那个举例子的代码并不会出现死锁的情况.

首先,答案是肯定的,不能释放,如果在这里释放锁了,那么中间的synchronized以及中间的代码就不会受到锁的保护了.

那么我们想一下,可重入锁,需要比不可重入锁额外多出哪些功能

1.判断当前加锁的线程是不是同一个线程

2.判断当前代码执行到的是第几层的锁,那么这个功能是怎么实现的呢?

其实很简单,持有一个"计数器"就可以了,让锁对象不光要记录是哪个线程持有锁,同时再通过一个整形变量记录当前这个线程加了几次锁,每遇到一个加锁操作,就计数器+1,每遇到一个解锁操作就-1,当计数器减为0的时候,才真正执行释放锁的操作,其他时候不释放锁.而类似于这个操作的操作,我们称它为"引用计数"

死锁的三种典型情况

1.一个线程,一把锁,但是是不可重入锁,该线程针对这个锁连续加锁两次,就会出现死锁

2.两个线程,两把锁,这两个线程先分别获取到一把锁,然后再同时尝试获取对方的锁

下面我们敲代码来理解一下死锁

首先,这是一个错误的代码

执行结果如下

实际上由刚才的分析可以知道,这里会出现死锁的情况,那么为什么这里和理论值不一样呢?

因为没有加Sleep

代码如上

执行结果如下

那么,为什么?为什么加了Sleep之后会不一样,究竟是Sleep改变了线程原有的样子,还是说Sleep恢复了线程原有的样子?

答案是后者

我们在使用多线程的时候会发现,程序运行的时间特别长了会经常出现一些问题,或者说当我们来气了多个线程他们分别执行几个任务,但是因为执行的任务的时间非常短,有时候CPU切换的时候会出现一系列的问题.

原因是当我们设置Sleep是,就等于告诉CPU,当前的线程不再运行,持有当前对象的锁.那么这个时候CPU就会切换到另外的线程了,这种操作在有些时候是非常好的.

3.N个线程M把锁

哲学家就餐问题

每个哲学家,主要做两件事

1.思考人生,会放下筷子

2.吃面.会拿起左手和右手的筷子

3.每个哲学家,什么时候思考人生,什么时候吃面条,都不好说

2.每个哲学家一旦想吃面条了,就会非常固执的完成吃面条的这个操作,如果此时,他的筷子被别人使用了,就会阻塞等待,而且等待的过程中不会放下手中已经拿着的的筷子

是否有办法去避免死锁呢?

先明确产生死锁的原因,死锁的必要条件

四个必要条件(缺一不可,只要破坏其中任意一个条件,就可以避免死锁)

1.互斥使用,一个线程获取到一把锁之后,别的线程不能获取到这个锁(我们实际使用的锁,一般都是互斥的(锁的基本特性))

2.不可抢占,锁只能是被持有者主动释放,而不是被其他线程直接抢走(锁的基本特性)

3.请求和保持,这一个线程尝试去获取多把锁,在获取第二把锁的过程中,会保持对第一把锁的获取状态(取决于代码结构)(很可能会影响到需求)

在获取第二把锁的同时会保持对第一把锁的状态,这里由于获取第二把锁的时候,并没有去释放第一把锁,所以就会出现阻塞等待

当我们将代码改成这样,即获取完第一把锁之后,并且将第一把锁释放掉,此时再去请求获取第二把锁,这样做是不会出现死锁的

4.循环等待.t1尝试获取 locker2,需要t2执行完,释放locker2;t2尝试获取locker1,需要t1执行完,释放locker1 (取决于代码结构)(解决死锁问题的最关键要点)

如果具体解决死锁问题,实际的方法有很多种(例:银行家算法(但不推荐,因为不接地气))

介绍一个更简单,也非常有效的解决死锁的办法

针对锁进行编号,并且规定加锁的顺序

比如,约定,每个线程如果要获取多把锁,必须先获取编号小的锁,后获取编号大的锁.只要所有线程加锁的顺序,都严格遵守上述顺序,就一定不会出现循环等待.

像这样,我们规定先让locker1加锁,然后让locker2加锁,按照指定顺序加锁,也就可以避免死锁的问题了

synchronized具体是采用了哪些锁策略

1.synchronized即是悲观锁,也是乐观锁.

2.synchronized即是重量级锁,也是轻量级锁.

3.synchronized重量级锁部分是基于系统的互斥锁实现的,轻量级锁部分是基于自旋锁实现的.

4.synchronized是非公平锁(不会遵守先来后到,锁释放了之后,哪个线程拿到锁,各凭本事).

5.synchronized是可重入锁(内部会记录哪个线程拿到了锁,记录引用次数).

6.synchronized不是读写锁.

synchronized内部实现策略(内部原理)

代码中写了一个synchronized之后,这里可能会产生一系列的"自适应的过程",锁升级(锁膨胀)

无锁->偏向锁->轻量级锁->重量级锁

偏向锁(懒汉模式思想的延伸)

不是真的加锁,而只是做了一个"标记".如果有别的线程来竞争锁了,才会真的加锁,如果没有别的线程竞争,就自始至终都不会真的加锁了.(加锁本身,有一定的开销,能不加就不加,非得是有人来竞争了,才会真的加锁)

偏向锁在没有其他人竞争的时候,就仅仅是一个简单的标记(非常轻量).一旦有别的线程尝试加锁,就会立刻把偏向锁升级为一个真正的加锁状态,让其他线程只能阻塞等待

轻量级锁

synchronized通过自旋的方式来实现轻量级锁,我这边把锁占据了,另一个线程就会按照自旋的方式,来反复查询当前的锁的状态是不是被释放了.但是,后续如果竞争这把锁的线程越来越多了(锁冲突更激烈了),就会从轻量级锁,升级成重量级锁

锁消除

编译器,会智能的判定,当前的这个代码,是否有必要加锁,如果你写了加锁,但实际上没有必要加锁,就会把加锁操作自动优化掉。

比如在单个线程中使用StringBuffer.

编译器进行优化,是要保证优化之后的逻辑和之前的逻辑是一致的

锁粗化

关于"锁的粒度"如果加锁操作里包含的实际要执行的代码越多,就认为锁的粒度越大.

//以下是一些伪代码
//锁的粒度小
for(.....){
    synchronized(this){
        count++;    
    }
}
//锁的粒度大
synchronized(this){
    for(.....){
        count++;    
    }
}

 

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

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

相关文章

alibaba EasyExcel 简单导出数据到Excel

导入依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>4.0.1</version> </dependency> 1、alibaba.excel.EasyExcel导出工具类 import com.alibaba.excel.EasyExcel; import …

揭秘,PyArmor库让你的Python代码更安全

PyArmor 概述: PyArmor 是一个用于加密和保护 Python 源代码的工具,旨在防止代码被逆向工程和未经授权的使用.通过将 Python 源代码编译为加密的字节码,PyArmor 提供了一种有效的方法来保护知识产权和敏感算法. 安装 pip install pyarmor安装完成后,可以通过以下命令验证安装…

SaaS联盟分销系统如何高效管理推广渠道的实用指南

随着众多SaaS企业不断向PLG模式转型&#xff0c;传统的推广方式&#xff0c;比如广告投放推流、第三方cookie数据追踪等方式的成本效益比低下&#xff0c;更多的SaaS企业选择可“低成本&#xff0c;高回报”的联盟营销策略。比如Figama、Unbounce、Looka这些中小型SaaS企业&…

SCI丨5分期刊,JCR一区

SCI&#xff0c;5分&#xff0c;JCR Q1&#xff0c;中科大类3小类2区 1 基于复杂网络与xxx能源汽车节能数值分析 2 基于热能损失优化的xxx与性能管理 3 基于xxxLCA技术的绿色制造工艺优化研究 4 基于xxx入侵检测技术的物联网智能制造监控系统设计 6 基于物联网技术xxx电力系…

鸿蒙认证值得考吗?

鸿蒙认证值得考吗&#xff1f; 鸿蒙认证&#xff08;HarmonyOS Certification&#xff09;是华为为了培养和认证开发者在鸿蒙操作系统&#xff08;HarmonyOS&#xff09;领域的专业技能而设立的一系列认证项目。这些认证旨在帮助开发者和企业工程师提升在鸿蒙生态中的专业技能…

通过混合栅极技术改善p-GaN功率HEMTs的ESD性能

来源&#xff1a;Improved Gate ESD Behaviors of p-GaN PowerHEMTs by Hybrid Gate Technology&#xff08;ISPSD 24年&#xff09; 摘要 本工作中&#xff0c;首次证明了混合栅极技术在不增加额外面积和寄生效应的前提下&#xff0c;能有效提升p-GaN HEMTs的栅极静电放电(E…

刷代码随想录有感(124):动态规划——最长公共子序列

题干&#xff1a; 代码&#xff1a; class Solution { public:int findLength(vector<int>& nums1, vector<int>& nums2) {vector<vector<int>>dp(nums1.size() 1, vector<int>(nums2.size() 1, 0));int res 0;for(int i 1; i <…

[单master节点k8s部署]16.监控系统构建(一)Prometheus介绍

prometheus prometheus是继k8s之后&#xff0c;第二个被托管到CNCF的项目&#xff0c;是一个开源的监控报警系统。 1.prometheus支持多维数据模型&#xff0c;每一个时间序列数据都由metric度量指标名称和它的标签label组成一组键值对。 2.Prometheus有自己的PromQL查询语言…

mac|Mysql WorkBench导入文件失败(修改编码)

⚠️&#xff1a;表格中有中文的不适用表格中有中文的不适用表格中有中文的不适用表格中有中文的不适用 我有一个excel表&#xff0c;想导入到mysql数据库中&#xff0c;但是Workbench的导入格式只支持csv&#xff0c;通过excel、wps将excel另存为csv文件进行导入 导入会因为编…

Swift 中强大的 Key Paths(键路径)机制趣谈(下)

概览 在上一篇博文 Swift 中强大的 Key Paths(键路径)机制趣谈(上)中,我们介绍了 Swift 语言中键路径机制的基础知识,并举了若干例子讨论了它的一些用武之地。 而在本文中我们将再接再厉,继续有趣的键路径大冒险,为 KeyPaths 画上一个圆满的句号。 在本篇博文中,您将…

高考服务系统

摘 要 每年有大批考生在进行填写高考志愿时并不很清楚自己的高考分数适合那些高校以及专业。高考考生面临着未被高校录取&#xff0c;被调剂专业&#xff0c;甚至可能复读的问题。若能让考生轻松查询到高校录取、高校专业、高校招生等相关信息&#xff0c;能减少很大一部分考生…

2007-2023年36家商业银行绿色信贷、期末贷款总额、银行总资产等相关指标数据(2023年无缺失)

2007-2023年36家商业银行绿色信贷数据&#xff08;2023年无缺失&#xff09; 1.时间&#xff1a;2007-2023年&#xff0c;2023年无缺失 2.来源&#xff1a;银行年报和社会责任报告 3.指标:绿色信贷余额、期末贷款总额、绿色信贷比率、总资产收益率、流动性比率、拨备覆盖率、…

svn忽略上传文件node_modules文件

文章目录 1.点击svn项目右键-》选中svn的属性2. 点击 新建3. 点击其他4. 选择属性 svn:global-ignores5. 输入忽略文件 1.点击svn项目右键-》选中svn的属性 2. 点击 新建 3. 点击其他 4. 选择属性 svn:global-ignores 5. 输入忽略文件

通信软件开发之业务知识:PON口割接什么意思?

一 PON口割接&#xff08;原创总结&#xff09; 在通信领域&#xff0c;PON口割接指的是对无源光网络&#xff08;Passive Optical Network&#xff0c;PON&#xff09;端口进行的切换或调整操作。简单来说&#xff0c;就是对光纤网络中的某个端口进行重新连接或重新分配&…

C++基础(四):C++入门(三)

通过前面的学习&#xff0c;我们已经掌握了一些最基本的C入门知识&#xff0c;这一篇博客我们主要聚焦于&#xff1a;C/C内存管理和C11的一些新特性&#xff0c;为后续深入学习做好铺垫。 目录 一、C/C内存管理 1.1 C/C内存分布 1.2 C语言中动态内存管理方式&#xff1a;ma…

漆包线行业生产管理革新:万界星空科技MES系统解决方案

一、引言 在科技日新月异的今天&#xff0c;万界星空科技凭借其在智能制造领域的深厚积累&#xff0c;为漆包线行业量身打造了一套先进的生产管理执行系统&#xff08;MES&#xff09;解决方案。随着市场竞争的加剧&#xff0c;漆包线作为电气设备的核心材料&#xff0c;其生产…

再回首,什么是人工智能?

文章目录 一、说明二、什么是人工智能&#xff1f;三、人工智能的类型&#xff1a;弱人工智能与强人工智能四、深度学习与机器学习五、生成模型的兴起六、人工智能应用6.1 语音识别6.2 客户服务6.3 计算机视觉6.4 供应链6.5 天气预报6.6 异常检测 七、人工智能的历史&#xff1…

AI赋能视创云展元宇宙展厅,打造沉浸式智能观展体验!

在AI人工智能迅猛发展的背景下&#xff0c;视创云展将元宇宙展厅与“AI智能助手”深度融合&#xff0c;这无疑是一次前瞻性的创新尝试&#xff0c;极大地丰富了展览展示的形式与内容&#xff0c;为参观者打造了一个既沉浸又智能的观展新纪元。 一、沉浸式体验 元宇宙展厅利用虚…

将excel表格转换为element table(下)

在‘将excel表格转换为element table(上)’我们把excel 转换后通过数据重构绑定到了element table上&#xff0c;现在要做的就是根据源文件进行行列进行合并操作 先看看最终处理的结果 这里在一步步分析实现步骤。 先分析一下合并的逻辑 大致思路理理如上。 思路有了接下来…

ALZET渗透泵怎么用之常见问题解答

ALZET渗透压泵是微型植入式泵&#xff0c;适用于小鼠&#xff0c;大鼠和其他实验室动物的研究。这些微型泵以连续和受控的速率输送药物&#xff0c;激素和其他测试剂&#xff0c;持续时间从一天到六周&#xff0c;无需外部连接或频繁处理。无人值守的操作消除了实验室人员重复夜…