【多线程】synchronized底层实现的方式

前言

在java 开发中对于锁的应用非常的常见,如果对于什么时候该用什么锁,以及锁实现的原理有所不知道的,或者面试过程中面试官问你不知道怎么回答的,欢迎来看下面的文章

1、synchronized和ReentrantLock的区别

在这里插入图片描述

2、synchronized的一些特性和底层原理的实现

2.1 synchronized 的锁升级过程

在 JVM 中,锁升级是不可逆的,即一旦锁被升级为下一个级别的锁,就无法再降级。

首先默认的无锁状态,当我们加锁以后,可能并没有多个线程去竞争锁,此时我们可以默认为只有一个线程要获取锁,即偏向锁,当锁转为偏向锁以后,被偏向的线程在获取锁的时候就不需要竞争,可以直接执行。

当确实存在少量线程竞争锁的情况时,偏向锁显然不能再继续使用了,但是如果直接调用重量级锁在轻量锁竞争的情况下并不划算,因为竞争压力不大,所以往往需要频繁的阻塞和唤醒线程,这个过程需要调用操作系统的函数去切换 CPU 状态从用户态转为核心态。因此,可以直接令等待的线程自旋,避免频繁的阻塞唤醒 ,此时升级为轻量级锁

当竞争加大时,线程往往要等待比较长的时间才能获得锁,此时在等待期间保持自旋会白白占用 CPU 时间,此时就需要升级为重量级锁,即 Monitor 锁,JVM 通过指令调用操作系统函数阻塞和唤醒线程。

2.2 synchronized的锁优化

2.2.1 自适应自旋锁

自旋锁依赖于 CAS,我们可以手动的设置 JVM 的自旋锁自旋次数,但是往往很难确定适当的自旋次数,如果自旋次数太少,那么可能会引起不必要的锁升级,而自旋次数太长,又会影响性能。在 JDK6 中,引入了自适应自旋锁的机制,对于同一把锁,当线程通过自旋获取锁成功了,那么下一次自旋次数就会增加,而相反,如果自旋锁获取失败了,那么下一次在获取锁的时候就会减少自旋次数。

2.2.2 锁消除

在一些方法中,有些加锁的代码实际上是永远不会出现锁竞争的,比如 Vector 和 Hashtable 等类的方法都使用 synchronized 修饰,但是实际上在单线程程序中调用方法,JVM 会检查是否存在可能的锁竞争,如果不存在,会自动消除代码中的加锁操作。

2.2.3 锁粗化

我们常说,锁的粒度往往越细越好,但是一些不恰当的范围可能反而引起更频繁的加锁解锁操作,比如在迭代中加锁,JVM 会检测同一个对象是否在同一段代码中被频繁加锁解锁,从而主动扩大锁范围,避免这种情况的发生。

2.3 synchronized的底层原理的实现

synchronized 意为同步,它可以用于修饰静态方法,实例方法,或者一段代码块。其中修饰代码块和方法有一点不同
它是一种可重入的对象锁。当修饰静态方法时,锁对象为类;当修饰实例方法时,锁对象为实例;当修饰代码块时,锁可以是任何非 null 的对象。由于其底层的实现机制,synchronized 的锁又称为监视器锁。

同步代码块
    public void synchronizedMethod3() {synchronized(this) {System.out.println("synchronizedMethod3");}}

反编译之后:
在这里插入图片描述

monitorenter      // 尝试获取对象锁  
...  
monitorexit       // 正常退出同步块(偏移量 15)  
goto 23  
monitorexit       // 异常退出同步块(偏移量 21)  
athrow  

这里的 monitorenter 与 monitorexit 即是线程获取 synchronized 锁的过程。

当线程试图获取对象锁的时候,根据 monitorenter 指令:

如果 Monitor 的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为 Monitor 的所有者;
如果线程已经占有该 monitor,只是重新进入,则进入 Monitor 的进入数加1(可重入); 如果其他线程已经占用了
monitor,则该线程进入阻塞状态,直到 Monitor 的进入数为0,再重新尝试获取 Monitor 的所有权; 当线程执行完以后,根据
monitorexit 指令:

当执行 monitorexit 指令后,Monitor 的进入数 -1; 如果 – 1 后 Monitor 进入数为 0,则该线程不再拥有这个锁,退出 monitor; 如果 – 1 后 Monitor 进入数仍不为0,则线程继续持有这个锁,重复上述过程直到使用完毕。

特点:

  • 显式生成 monitorenter 和 monitorexit 指令 进行获取锁和释放锁
  • 异常表中注册两个退出路径,确保锁必然释放
同步方法
public synchronized void syncMethod() {  System.out.println("同步方法");  
}  
关键字节码:flags: ACC_PUBLIC, ACC_SYNCHRONIZED  

通过 ACC_SYNCHRONIZED 标志隐式实现锁机制是 Java 中 synchronized 关键字的核心实现原理。 在 Java
虚拟机(JVM)中,synchronized 关键字修饰的方法或代码块会通过在方法的 access_flags 字段中添加
ACC_SYNCHRONIZED 标志来标识该方法为同步方法。这个标志告诉 JVM,该方法需要进行同步操作。

同步机制的隐式实现 当一个方法被标记为 ACC_SYNCHRONIZED 时,JVM 在方法调用时会隐式地执行加锁和解锁操作。具体来说:

加锁:在方法执行前,线程需要获取与该方法相关的监视器锁(monitor)。如果锁已被其他线程持有,则当前线程会被阻塞,直到锁被释放。
解锁:当方法正常完成或抛出异常时,锁会被自动释放。这确保了方法的原子性和可见性。

特点:

  • 通过 ACC_SYNCHRONIZED 标志隐式实现锁机制
  • JVM 在方法调用和返回时自动插入锁操作
实现原理

synchronized 是对象锁,在 JDK6 引入锁升级机制后,synchronized 的锁实际上分为了偏向锁、轻量级锁和重量级锁三种,这三者都依赖于对象头中 MarkWord 的数据的改变。

对象

每个 Java 对象在内存中分为 对象头(Header) 、 实例数据(Instance Data) 和 对齐填充(Padding)
在这里插入图片描述
对象头是实现锁的核心区域,其结构如下

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/758316d58cc84158bcca7804c211126d.png

其中markWord的样子如下:
在这里插入图片描述

2.3 重量级锁与监视器

synchronized 的对象锁是基于监视器对象 Monitor 实现的,而根据上文,我们知道锁信息存储于对象自己的 MarkWord 中,那么 Monitor 和 对象又是什么关系呢?

实际上,在对象在创建之初就会在 MarkWord 中关联一个 Monitor 对象 ,当锁升级到重量级锁时,markWord存储指向 Monitor 对象的指针。

Monitor 对象在 JVM 中基于 ObjectMonitor 实现,代码如下:

ObjectMonitor() {_header       = NULL;_count        = 0; // 持有锁次数_waiters      = 0,_recursions   = 0;_object       = NULL;_owner        = NULL; // 当前持有锁的线程_WaitSet      = NULL; // 等待队列,处于wait状态的线程,会被加入到_WaitSet_WaitSetLock  = 0 ;_Responsible  = NULL ;_succ         = NULL ;_cxq          = NULL ;FreeNext      = NULL ;_EntryList    = NULL ; // 阻塞队列,处于等待锁block状态的线程,会被加入到该列表_SpinFreq     = 0 ;_SpinClock    = 0 ;OwnerIsThread = 0 ;
}

ObjectMonitor 中有两个队列,_WaitSet 和 _EntryList,用来保存 ObjectWaiter 对象列表( 每个等待锁的线程都会被封装成 ObjectWaiter 对象 ),_owner 指向持有 ObjectMonitor 对象的线程,当多个线程同时访问一段同步代码时:
首先会进入 _EntryList 集合,当线程获取到对象的 Monitor 后,进入 _Owner 区域并把 monitor 中的 owner 变量设置为当前线程,同时 monitor中的计数器 count 加1;
若线程调用 wait() 方法,将释放当前持有的 monitor,owner 变量恢复为 null,count 自减1,同时该线程进入 WaitSet集合中等待被唤醒;
若当前线程执行完毕,也将释放 Monitor 并复位 count 的值,以便其他线程进入获取 Monitor;
这也解释了为什么 notify() 、notifyAll()和wait() 方法会要求在同步块中使用,因为这三个方法都需要获取 Monitor 对象,而要获取 Monitor,就必须使用 monitorenter指令。

2.4 synchronized线程调用时的阻塞场景


public class Example {public synchronized void methodA() { ... }public synchronized void methodB() { ... }
}

/线程1调用obj.methodA(),线程2调用obj.methodB() → 线程2阻塞,直到methodA释放锁
所以再用的时候尽可能的缩小锁的范围,代码块加锁

总结

每个对象在内存中分为三个部分示例数据(存放类信息)、对象头和对齐填充,其中对象头有个重要的组成部分markWord,markWord中存储一些锁相关的引用,通过markWord中的锁标志位,我们可以清楚的知道锁的级别,是重量级锁,还是轻量级锁,还是偏向锁,当是重量级锁的时候,markWord中存储了对监视器Monitor 对象的引用,synchronized 的对象锁是基于监视器对象 Monitor 实现的,在对象在创建之初就会在 MarkWord 中关联一个 Monitor 对象 ,当锁升级到重量级锁时,markWord存储指向 Monitor 对象的指针。而要获取 Monitor,就必须使用 monitorenter指令,可重入的表现是,Monitor里面有一个count,一次monitorenter后count 加1,一次monitorExit 后count减1

参考文章:https://cloud.tencent.com/developer/article/2120268?pos=comment

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

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

相关文章

Pytorch中Tensorboard的学习

1、Tensorboard介绍 TensorBoard 是 TensorFlow 开发的一个可视化工具,用于帮助用户理解和调试机器学习模型的训练过程。尽管它最初是为 TensorFlow 设计的,但通过 PyTorch 的 torch.utils.tensorboard 模块,PyTorch 用户也可以方便地使用 Te…

ETL 自动化:提升数据处理效率与准确性的核心驱动力

在数字化转型的浪潮中,数据已成为企业战略资产,高效处理数据的能力直接关系到企业的竞争力。ETL(Extract, Transform, Load)自动化作为数据处理领域的关键技术,正逐渐成为企业在数据时代脱颖而出、实现高效运营与精准决…

std::endl为什么C++ 智能提示是函数?

在使用vscode 的C智能提示后&#xff0c;输入endl 后&#xff0c;提示的却是std::endl(basic_ostream<CharT, Traits> &os), 感觉比较奇怪&#xff0c;各种代码里都是直接用的std::endl 啊&#xff0c; 这里怎么变成函数了呢&#xff1f; 在 C 中&#xff0c;std::en…

简洁、实用、无插件和更安全为特点的WordPress主题

简站WordPress主题是一款以简洁、实用、无插件和更安全为特点的WordPress主题&#xff0c;自2013年创立以来&#xff0c;凭借其设计理念和功能优势&#xff0c;深受用户喜爱。以下是对简站WordPress主题的详细介绍&#xff1a; 1. 设计理念 简站WordPress主题的核心理念是“崇…

数据结构篇:空间复杂度和时间复杂度

目录 1.前言&#xff1a; 1.1 学习感悟 1.2 数据结构的学习之路(初阶) 2.什么是数据结构和算法 2.1 数据结构和算法的关系 2.2 算法的重要性 2.3 如何衡量算法的好坏 3.时间复杂度 3.1 时间复杂度的概念 3.2 大O的渐进表示法 O() 4.空间复杂度 5. 常见的时间复杂度和…

node-ddk,electron,截屏封装(js-web-screen-shot)

node-ddk 截屏封装(js-web-screen-shot) https://blog.csdn.net/eli960/article/details/146207062 也可以下载demo直接演示 http://linuxmail.cn/go#node-ddk 感谢/第三方 本截屏工具, 使用的是: js-web-screen-shot https://www.npmjs.com/package/vue-web-screen-shot…

泰坦军团携手顺网旗下电竞连锁品牌树呆熊 共创电竞新纪元

在电竞行业的浪潮中&#xff0c;品牌之间的战略合作愈发成为推动市场前行的重要动力。最近&#xff0c;电竞显示器领域领军品牌泰坦军团高层领导出席顺网旗下电竞连锁品牌树呆熊十周年盛典。会议现场&#xff0c;双方高层领导宣布泰坦军团与树呆熊正式达成战略合作伙伴关系。 在…

HandyJSON原理

HandyJSON 的优势 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式, 应用广泛. 在 App 的使用过程中, 服务端给移动端发送的大部分都是 JSON 数据, 移动端需要解析数据才能做进一步的处理. 在解析JSON数据这一块, 目前 Swift 中流行的框架基本上是 SwiftyJSON, …

信号的产生和保存

信号的产生 信号就是操作系统对用户操作做出的反应&#xff0c;但它的本质就是往操作系统写入信号&#xff0c;这是由操作系统的结构决定的。通过修改比特位来告诉操作系统接收信号和传了几号信号。 也正是因为我们身为用户无法亲自修改内核数据&#xff0c;所以我们需要通过操…

在C++ Qt中集成Halcon窗口并实现跨平台兼容和大图加载

目录 1. Halcon窗口嵌入Qt Widget 2. 处理大图加载 3. 多线程优化显示 4. 跨平台兼容性 1. Halcon窗口嵌入Qt Widget 将Halcon的HWindow控件嵌入到Qt的QWidget容器中,利用系统原生句柄实现跨平台。 #include <HalconCpp.h> #include <QWidget>class HalconWi…

深度学习技术与应用的未来展望:从基础理论到实际实现

深度学习作为人工智能领域的核心技术之一&#xff0c;近年来引起了极大的关注。它不仅在学术界带来了革命性的进展&#xff0c;也在工业界展现出了广泛的应用前景。从图像识别到自然语言处理&#xff0c;再到强化学习和生成对抗网络&#xff08;GAN&#xff09;&#xff0c;深度…

蓝光三维扫描技术:汽车零部件检测的精准高效之选

——汽车方向盘配件、保险杠塑料件、钣金件检测项目 汽车制造工业的蓬勃发展&#xff0c;离不开强大的零部件制造体系作支撑。汽车零部件作为汽车工业的基础&#xff0c;其设计水平、制造工艺、质量控制手段逐渐与国际标准接轨&#xff0c;对于零部件面差、孔位、圆角、特征线…

数据库联表Sql语句建一个新表(MySQL,Postgresql,SQL server)

数据库联表Sql语句建一个新表(MySQL,Postgresql,SQL server) 如果你想基于 SELECT USERS.ID,USERS.NAME,USERS.EMAIL,USERS.ID_CARD,USERS.V_CARD,USERS.ADDRESS,v_card.type,v_card.amount FROM USERS JOIN v_card on USERS.V_CARDv_card.v_card 这个查询结果创建一个新表&am…

六十天前端强化训练之第三十天之深入解析Vue3电商项目:TechStore全栈实践(文结尾附有源代码)

欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗&#xff0c;谢谢大佬&#xff01; 目录 深入解析Vue3电商项目&#xff1a;TechStore全栈实践 一、项目架构设计 二、核心功能实现 三、组合式API深度实践 四、性能优化实践 五、项目扩展方向 六、开发经验总结…

【人工智能】机器学习中的评价指标

机器学习中的评价指标 在机器学习中&#xff0c;评估指标&#xff08;Evaluation Metrics&#xff09;是衡量模型性能的工具。选择合适的评估指标能够帮助我们更好地理解模型的效果以及它在实际应用中的表现。 一般来说&#xff0c;评估指标主要分为三大类&#xff1a;分类、…

不同机床对螺杆支撑座的要求有哪些不同?

螺杆支撑座是机械设备中重要的支撑部件&#xff0c;其选择直接影响到设备的稳定性和使用寿命&#xff0c;尤其是在机床中&#xff0c;不同的机床对螺杆支撑座的要求也是不同的。 1、精度&#xff1a;精密测量用的基准平面和精密机床机械的检验测量设备&#xff0c;需要使用高精…

在Spring Boot中,可以通过实现一些特定的接口来拓展Starter

在Spring Boot中&#xff0c;开发者可以通过实现一些特定的接口来拓展Starter。这些接口允许开发者自定义Spring Boot应用程序的配置和行为&#xff0c;从而创建功能丰富且易于使用的Starter。以下是一些关键的接口&#xff0c;用于拓展Starter&#xff1a; EnvironmentPostPro…

深入理解 tree 命令行工具:目录结构可视化的利器

文章目录 前言1. 什么是 tree 命令&#xff1f;安装 tree 2. tree 的基本用法显示当前目录的树状结构显示指定目录的树状结构 3. tree 的常用选项3.1 显示隐藏文件3.2 排除特定目录或文件3.3 限制递归深度3.4 显示文件大小3.5 显示文件的权限信息3.6 将输出保存到文件 4. 实际应…

Federated learning client selection algorithm based on gradient similarity阅读

基于梯度相似性的联邦学习客户端选择算法 Abstract 摘要introduction**背景****目的****结论****结果****讨论****思路** 链接&#xff1a;https://link.springer.com/article/10.1007/s10586-024-04846-0 三区 Abstract 摘要 联邦学习&#xff08;FL&#xff09;是一种创新的…

【测试工具】如何使用 burp pro 自定义一个拦截器插件

在 Burp Suite 中&#xff0c;你可以使用 Burp Extender 编写自定义拦截器插件&#xff0c;以拦截并修改 HTTP 请求或响应。Burp Suite 支持 Java 和 Python (Jython) 作为扩展开发语言。以下是一个完整的流程&#xff0c;介绍如何创建一个 Burp 插件来拦截请求并进行自定义处理…