synchronized 使用及实现原理

synchronized 关键字

如何使用

synchronized 关键字的使用方式主要有下面 3 种:

  1. 修饰实例方法

  2. 修饰静态方法

  3. 修饰代码块

1、修饰实例方法 (锁当前对象实例)

给当前对象实例加锁,进入同步代码前要获得 当前对象实例的锁

synchronized void method() {//业务代码
}

此时,synchronized加锁的对象就是这个方法所在实例的本身。

2、修饰静态方法 (锁当前类)

给当前类加锁,会作用于这个类的所有对象实例

这是因为静态成员不属于任何一个实例对象,归整个类所有,不依赖于类的特定实例,被类的所有实例共享。

synchronized static void method() {//业务代码
}

3、修饰代码块 (锁指定对象/类)

对括号里指定的对象/类加锁:

  • synchronized(object) 给对象加锁

  • synchronized(类.class) 给类加锁

synchronized(this) {//业务代码
}

 


synchronized关键字是如何对一个对象加锁实现代码同步的呢?

synchronized原理

synchronized 底层实现原理

底层原理就是,通过monitorenter 和 monitorexit 来完成同步机制,所以我们不用通过 lock 和 unlock 实现上锁和释放锁的过程。

当 synchronized 修饰方法的时候,JVM 采用ACC_SYNCHRONIZED标记符来实现同步,这个标识指明了该方法是一个同步方法。

synchronized修饰同步方法


在JVM中,对象在内存中存储的布局可以分为三个区域,分别是对象头、实例数据以及填充数据。

  • 实例数据 存放类的属性数据信息,包括父类的属性信息,这部分内存按4字节对齐。

  • 填充数据 由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。

  • 对象头 在HotSpot虚拟机中,对象头又被分为两部分,分别为:Mark Word(标记字段)、Class Pointer(类型指针)。如果是数组,那么还会有数组长度。对象头是本章内容的重点,下边详细讨论。

对象头

在对象头的Mark Word中主要存储了对象自身的运行时数据,例如哈希码、GC分代年龄、锁状态、线程持有的锁、偏向线程ID以及偏向时间戳等。同时,Mark Word也记录了对象和锁有关的信息。

当对象被synchronized关键字当成同步锁时,和锁相关的一系列操作都与Mark Word有关。Mark Word在不同锁状态下存储的内容有所不同。

GC标记是垃圾回收机制

可以看到重量级锁对象头的MarkWord中存储了指向Monitor对象的指针,那么什么是Monitor?

Monitor对象

Monitor对象被称为管程或者监视器锁。在Java中,每一个对象实例都会关联一个 Monitor 对象。这个Monitor对象既可以与对象一起创建销毁,也可以在线程试图获取对象锁时自动生成。当这个Monitor对象被线程持有后,它便处于锁定状态。

Monitor是由ObjectMonitor实现的,它是一个使用C++实现的类,主要数据结构如下:

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 ; // 阻塞队列,线程被唤醒后根据决策判读是放入cxq还是EntryListFreeNext      = NULL ;_EntryList    = NULL ; // 没有抢到锁的线程会被放到这个队列_SpinFreq     = 0 ;_SpinClock    = 0 ;OwnerIsThread = 0 ;}
 

ObjectMonitor中有五个重要部分,分别为ower,WaitSet,cxq,EntryList和count。

  • _ower 用来指向持有monitor的线程,它的初始值为NULL,表示当前没有任何线程持有monitor。当一个线程成功持有该锁之后会保存线程的ID标识,等到线程释放锁后_ower又会被重置为NULL;

  • _WaitSet 调用了锁对象的wait方法后的线程会被加入到这个队列中;如果调用锁对象的wait()方法,线程会释放当前持有的monitor,并将owner变量重置为NULL,且count减1,同时该线程会进入到_WaitSet集合中等待被唤醒。

  • _cxq 是一个阻塞队列,线程被唤醒后根据决策判读是放入cxq还是EntryList;

  • _EntryList 没有抢到锁的线程会被放到这个队列;

  • count 用于记录线程获取锁的次数,成功获取到锁后count会加1,释放锁时count减1。

问题:synchronized 锁住的到底是什么?

答:是 monitor enter 和 monitor exit

MarkWord 中存储了指向 Monitor 对象的指针,Monitor 是由ObjectMonitor 实现

ObjectMonitor 原理

当获取 monitor 对象的线程进入 _ower 区的时候, _count+1。当调用 wait()方法后,释放 monitor 对象, _count - 1,退出 _ower 区。

当对象的Monitor的计数器count为0 的时候,线程可以取得Monitor,并将count设置为1,即获得该对象的锁,表示只有这个线程可以对该对象进行操作。如果当前线程已经拥有该对象monitor的持有权,那它可以重入这个 monitor ,计数器的值也会加 1。而当执行monitorexit指令时,锁的计数器会减1。

如果对象的Monitor的count为1时,那么当前线程获取锁失败将被阻塞并进入到_EntryList中,直到等待的锁被释放为止。也就是说,当所有相应的monitorexit指令都被执行,计数器的值减为0,执行线程将释放 monitor(锁),其他线程才有机会持有 monitor 。

流程

执行 monitorenter 获取锁

所以当了解完原理后,就能知道 monitor 是与同步有关系,所以synchronized 锁住的是:

  • monitorenter,在判断拥有同步标识 ACC_SYNCHRONIZED 抢先进入此方法的线程会优先拥有 Monitor 的 owner ,此时计数器 +1。

  • monitorexit,当执行完退出后,计数器 -1,归 0 后被其他进入的线程获得。

锁的升级

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

锁只能由这个方向进行升级,不可以逆着来。

偏向锁

由于大部分情况下啊,一般都是同一线程反复获得锁,每一次获得锁的过程中,需要获得锁和解锁的操作,所以,如果只有一个线程的话,那可以创建一种锁,当线程获得锁之后,就不进行解锁操作了,每次这个线程进入或退出同步快的时候,只需要比较下线程id就可以了

偏向锁加锁

当一个线程访问同步块的时候(同一时刻只有一个线程能执行同步块之中的代码),会在对象头和栈帧中存储锁偏向的线程id,如果该线程再进入的时候,只需要比较下线程id就可以。

偏向锁撤销
  • 如果原来持有偏向锁线程已经 退出同步代码块或者已经死亡,那么偏向锁可以直接撤销,转变为无锁状态。

  • 如果还在代码块之中,那么偏向锁升级为轻量级锁,原来的线程仍持有锁,其他进程需要cas,来竞争锁

优点

保证在只有一个线程的情况下,线程获得锁之后,该线程进入或退出同步代码块不需要进行cas操作,如果有多个锁竞争,那么锁会进行升级。

轻量级锁

轻量级锁是一种Java中实现线程同步的锁,它比重量级锁更高效,但也需要一定的条件才能使用。轻量级锁的加锁和撤销过程如下:

加锁过程

当一个线程要执行同步代码块时,它会先在自己的栈帧中创建一个锁记录,然后把对象头中的Mark Word复制到锁记录中,这叫做Displaced Mark Word。接着,线程会用CAS操作尝试把对象头中的Mark Word替换成指向锁记录的指针。如果成功,说明线程获得了轻量级锁,可以继续执行同步代码块。如果失败,说明有其他线程也在竞争这个锁,那么当前线程就会进行自旋,即不断重试CAS操作,直到成功或者超过一定次数。

撤销过程:

当一个线程执行完同步代码块时,它会用CAS操作尝试把对象头中的Mark Word恢复成原来的Displaced Mark Word。如果成功,说明没有其他线程竞争该锁,轻量级锁就被释放了。如果失败,说明有其他线程在自旋等待该锁,那么当前线程就会通知其他线程停止自旋,然后把锁升级为重量级锁,释放该锁后唤醒一个等待的线程。

具体CAS过程

CAS(Compare And Swap)是一种原子操作,它可以保证多个线程同时对同一个内存地址进行更新时的正确性。CAS操作需要三个参数:内存地址、期望值和新值。

CAS操作的过程是这样的:

  • 首先,从内存地址中读取当前值,与期望值进行比较,如果相等,说明没有其他线程修改过该内存地址,那么就用新值替换当前值,并返回成功。

  • 如果不相等,说明有其他线程修改过该内存地址,那么就放弃替换,并返回失败。

在轻量级锁的加锁和撤销过程中,CAS操作的内存地址就是对象头中的Mark Word,期望值就是Displaced Mark Word,新值就是指向锁记录的指针或者原来的Displaced Mark Word。CAS操作可以保证只有一个线程能成功获取或释放轻量级锁,其他线程则需要自旋或者阻塞。

轻量级锁优缺点

轻量级锁的优点是可以避免线程的阻塞和切换,因为得不到锁的线程不会被挂起,而是进行自旋,如果锁释放,可以第一时间知道,提高程序的响应速度。轻量级锁的缺点是如果一直不能获取到锁,长时间的自旋会造成CPU消耗。轻量级锁适用于少量线程竞争锁对象,且线程持有锁的时间不长,追求响应速度的场景。

总结

synchronized 同步语句块的实现使用的是 monitorentermonitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。

synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法。

不过两者的本质都是对对象监视器 monitor 的获取。

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

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

相关文章

Vitis HLS 学习笔记--AXI_STREAM_TO_MASTER

目录 1. 简介 2. 示例 2.1 示例功能介绍 2.2 示例代码 2.3 顶层函数解释 2.4 综合报告&#xff08;HW Interfaces&#xff09; 2.5 关于TKEEP和TSTRB 2.6 综合报告&#xff08;SW I/O Information&#xff09; 3. 总结 1. 简介 本文通过“<Examples>/Interface…

pytest(二)

1.pytest-html⽣成报告 Pytest-HTML 是⼀个插件&#xff0c;它可以⽣成漂亮且易于阅读的 HTML 测试报告。下⾯是使⽤ pytest-html ⽣成报告的步骤&#xff1a; 1. 安装 pytest-html 插件&#xff1a; pip install pytest-html 2. 运⾏测试并⽣成报告 pytest --htmlr…

5月13号作业

使用消息队列实现的2个终端之间的互相聊天 并使用信号控制消息队列的读取方式&#xff1a; 当键盘按ctrlc的时候&#xff0c;切换消息读取方式&#xff0c;一般情况为读取指定编号的消息&#xff0c;按ctrlc之后&#xff0c;指定的编号不读取&#xff0c;读取其他所有编号的消息…

CCF-Csp算法能力认证,202209-1如此编码(C++)含解析

前言 推荐书目&#xff0c;在这里推荐那一本《算法笔记》&#xff08;胡明&#xff09;&#xff0c;需要PDF的话&#xff0c;链接如下 「链接&#xff1a;https://pan.xunlei.com/s/VNvz4BUFYqnx8kJ4BI4v1ywPA1?pwd6vdq# 提取码&#xff1a;6vdq”复制这段内容后打开手机迅雷…

python内置函数exec()和eval()区别

在Python中&#xff0c;eval() 和 exec() 都是内置函数&#xff0c;用于执行存储在字符串或对象中的Python代码&#xff0c;但它们之间也有一些区别。 eval() 语法&#xff1a;eval(expression, globalsNone, localsNone) expression&#xff1a;需要求值的字符串表达式。可…

到底考不考CISP?纠结的看过来

专业认证如CISP&#xff08;注册信息安全专业人员&#xff09;成为了衡量专业水平的重要标准。 CISP的含金量懂的都懂&#xff0c;然而&#xff0c;是否要投入时间、精力和金钱去追求这样一个认证&#xff0c;对于许多人来说&#xff0c;依然是一个值得深思的问题。 那么到底…

第十一届蓝桥杯大赛软件类决赛 Java C 组

文章目录 发现宝藏【考生须知】试题 A: 美丽的 2试题 B: 合数个数试题 C: 扩散试题 D: 阶乘约数试题 E: 本质上升序列试题 F 天干地支试题 G 皮亚诺曲线距离试题 H 蓝肽子序列试题 I: 画廊试题 J 答疑 发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&a…

Linux 操作系统多路IO复用

1.多路IO复用 多路I/O复用是通过一种机制&#xff0c;可以监视多个描述符&#xff0c;一旦某个描述符就绪&#xff08;一般是读就绪或者写就绪&#xff09;&#xff0c;能够通知程序进行相应的读写操作。 这个机制能够通过select/poll/eroll等来使用。这些函数都可以同时监视多…

文件流-二进制文件(中北大学-程序设计基础(2))

目录 题目 源码 结果示例 题目 建立两个二进制磁盘文件f1.dat,f2.dat&#xff0c;编程实现以下工作&#xff1a; &#xff08;1&#xff09;将20个整数&#xff08;可在程序中初始化&#xff09;&#xff0c;分别存放到两个磁盘文件中&#xff0c;前10个放到f1.dat中&…

Linux下网络命令

目录 需求1-查看本机是否存在22端口解法1解法2解法3 需求2-查看其他主机是否存在22端口解法1解法2解法3 需求3-查看TCP连接解法1/2 需求4-统计80端口tcp连接次数解法 需求5-查看总体网络速度解法 需求6-查看进程流量解法 需求7-dns解法 需求8-traceroute到baidu解法 需求9-查看…

任务通知理论和使用

文章目录 一、任务通知是什么&#xff1f;1.1任务通知的优势1.2任务通知的限制1.3通知状态和通知值 二、任务通知的使用2.1任务通知使用_轻量级信号量2.2任务通知使用_轻量级队列2.3任务通知使用_轻量级事件组 一、任务通知是什么&#xff1f; 我们使用队列、信号量、事件组等…

浅析视频汇聚EasyCVR视频融合云平台在机场安防智能检测建设中的应用

一、背景 机场作为国家交通枢纽和对外开放的窗口&#xff0c;其安全运行直接关系到乘客的生命安全、国家形象以及社会经济稳定。随着全球航空业的快速发展和人们出行需求的持续增长&#xff0c;机场作为重要的交通枢纽&#xff0c;其客流量和货运量均呈现出快速增长的态势。然而…

HCIP的学习(16)

BGP的状态机 ​ OSPF的状态机是在描述整个协议的完整工作过程&#xff0c;而BGP的状态机仅描述的是对等体关系建立过程中的状态变化。-----因为BGP将邻居建立过程以及BGP路由收发过程完全隔离。 ​ IGP协议在启动后&#xff0c;需要通过network命令激活接口&#xff0c;从而使…

SD-WAN供应商选择指南

企业网络日益演变&#xff0c;尤其是跨国企业、出海电商和外贸企业&#xff0c;其网络需求变得愈发复杂多样。SD-WAN技术因此备受瞩目&#xff0c;成为连接分支机构和数据中心的关键解决方案。然而&#xff0c;市面上的SD-WAN供应商众多&#xff0c;如何选择适合的服务商成为了…

gitignore配置不生效记录

第一种可能性&#xff1a; 在你所有的文件都通过了git add . 命令的情况下&#xff0c;使用指令git rm -r --cached .进行缓存清除&#xff0c;完成之后&#xff0c;再次通过git add . 然后通过git status去看提交的文件都有哪些。 第二种可能性 如果上面的不行就是你添加的…

【机器学习】Scikit-Learn:Python机器学习的瑞士军刀

Scikit-Learn&#xff1a;Python机器学习的瑞士军刀 一、Scikit-Learn简介二、Scikit-Learn的核心功能数据预处理模型选择模型评估模型部署 三、Scikit-Learn的中文社区与API四、代码实例&#xff1a;使用Scikit-Learn进行鸢尾花数据集分类 在当今这个数据驱动的时代&#xff0…

论文《Deep graph tensor learning for temporal link prediction》阅读

论文《Deep graph tensor learning for temporal link prediction》阅读 论文概况IntroductionRelated work动态图表示学习图张量表示 Preliminary张量生成建模 深度图张量学习模型A.基于图紧凑的空间表示B.时间模式表示C.时空特征聚合D.损失函数 实验数据集对比实验消融实验参…

鸿蒙ArkUI开发:常用布局【交叉轴】

交叉轴 垂直于主轴方向的轴线。Row容器交叉轴为纵向&#xff0c;Column容器交叉轴为横向。通过alignItems属性设置子元素在交叉轴&#xff08;排列方向的垂直方向&#xff09;上的对齐方式alignSelf属性用于控制单个子元素在容器交叉轴上的对齐方式&#xff0c;其优先级高于al…

M 有效算法

M 有效算法 本题考验二分知识&#xff0c;思路是二分k的取值&#xff0c;就按第一组样例来说当我们k取值为1的时候我们遍历数组想让|8-x|<k1的话x的取值范围是7-9&#xff0c;想让|3-x|<k2的话x的取值范围是1-5&#xff0c;两者x的区间不重合&#xff0c;说明肯定没有x能…

BGP—边界网关协议

BGP 动态路由协议可以按照工作范围分为IGP以及EGP。IGP工作在同一个AS内&#xff0c;主要用来发现和计算路由&#xff0c;为AS内提供路由信息的交换&#xff1b;而EGP工作在AS与AS之间&#xff0c;在AS间提供无环路的路由信息交换&#xff0c;BGP则是EGP的一种。 BGP是一…