Java之Synchronized与锁升级
Synchronized与锁升级
一、概述
在多线程并发编程中 synchronized 一直是元老级角色,很多人都会称呼它为重量级锁。但是,随着 Java SE 1.6 对 synchronized 进行了各种优化之后,有些情况下它就并不那么重了。
本文详细介绍 Java SE 1.6 中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁,以及锁的存储结构和升级过程。
二、实现同步的基础
Java 中的每个对象都可以作为锁,具体变现为以下3中形式:
- 对于普通同步方法,锁是当前实例对象
- 对于静态同步方法,锁是当前类的 Class 对象
- 对于同步方法块,锁是 synchronized 括号里配置的对象
一个线程试图访问同步代码块时,必须获取锁,在退出或者抛出异常时,必须释放锁。
三、实现方式
JVM 基于进入和退出 Monitor 对象来实现方法同步和代码块同步,但是两者的实现细节不一样。
- 代码块同步:通过使用 monitorenter 和 monitorexit 指令实现的
- 同步方法:ACC_SYNCHRONIZED 修饰
monitorenter 指令是在编译后插入到同步代码块的开始位置,而 monitorexit 指令是在编译后插入到同步代码块的结束处或异常处,对于同步方法,个人觉得也是类似的原理,进入方法前添加一个 monitorenter 指令,退出方法后条件一个 monitorexit 指令。
为了证明 JVM 的实现方式,下面通过反编译代码来证明:
public class Demo {public void f1() {synchronized (Demo.class) {System.out.println("Hello World.");}}public synchronized void f2() {System.out.println("Hello World.");}}
编译之后的字节码如下(只摘取了方法的字节码):
public void f1();descriptor: ()Vflags: ACC_PUBLICCode:stack=2, locals=3, args_size=10: ldc #2 // class me/snail/base/Demo2: dup3: astore_14: monitorenter5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;8: ldc #4 // String Hello World.10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V13: aload_114: monitorexit15: goto 2318: astore_219: aload_120: monitorexit21: aload_222: athrow23: returnException table:from to target type5 15 18 any18 21 18 anyLineNumberTable:line 6: 0line 7: 5line 8: 13line 9: 23StackMapTable: number_of_entries = 2frame_type = 255 /* full_frame */offset_delta = 18locals = [ class me/snail/base/Demo, class java/lang/Object ]stack = [ class java/lang/Throwable ]frame_type = 250 /* chop */offset_delta = 4public synchronized void f2();descriptor: ()Vflags: ACC_PUBLIC, ACC_SYNCHRONIZEDCode:stack=2, locals=1, args_size=10: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc #4 // String Hello World.5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnLineNumberTable:line 12: 0line 13: 8
先说 f1() 方法,发现其中一个 monitorenter 对应了两个 monitorexit,这是不对的。但是仔细看 #15: goto 语句,直接跳转到了 #23: return 处,再看 #22: athrow 语句发现,原来第二个 monitorexit 是保证同步代码块抛出异常时锁能得到正确的释放而存在的,这就理解了。
综上:发现同步代码块是通过 monitorenter 和 monitorexit 来实现的,同步方法是加了一个 ACC_SYNCHRONIZED 修饰来实现的。
四、优化后synchronized锁的分类
级别从低到高依次是:
- 无锁状态
- 偏向锁状态
- 轻量级锁状态
- 重量级锁状态
锁可以升级,但不能降级。即:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁是单向的。
下面看一下每个锁状态时,对象头中的 MarkWord 这一个字节中的内容是什么。
以32位系统为例:
1、无锁状态
25bit | 4bit | 1bit(是否是偏向锁) | 2bit(锁标志位) |
---|---|---|---|
对象的hashCode | 对象分代年龄 | 0 | 01 |
这里的 hashCode 是 Object#hashCode 或者 System#identityHashCode 计算出来的值,不是用户覆盖产生的 hashCode。
2、偏向锁状态
偏向锁:单线程竞争,当线程A第一次竞争到锁时,通过修改MarkWord中的偏向线程ID、偏向模式。如果不存在其他线程竞争,那么持有偏向锁的线程将永远不需要进行同步。
主要作用:
● 当一段同步代码一直被同一个线程多次访问,由于只有一个线程那么该线程在后续访问时便会自动获得锁
● 同一个老顾客来访,直接老规矩行方便
结论:
● HotSpot的作者经过研究发现,大多数情况下:在多线程情况下,锁不仅不存在多线程竞争,还存在由同一个线程多次获得的情况,偏向锁就是在这种情况下出现的,它的出现是为了解决只有一个线程执行同步时提高性能。
● 偏向锁会偏向于第一个访问锁的线程,如果在接下来的运行过程中,该锁没有被其他线程访问,则持有偏向锁的线程将永远不需要出发同步。也即偏向锁在资源在没有竞争情况下消除了同步语句,懒得连CAS操作都不做了,直接提高程序性能。
理论落地:
技术实现:
偏向锁JVM命令:
偏向锁的撤销:
● 当有另外一个线程逐步来竞争锁的时候,就不能再使用偏向锁了,要升级为轻量级锁,使用的是等到竞争出现才释放锁的机制
● 竞争线程尝试CAS更新对象头失败,会等到全局安全点(此时不会执行任何代码)撤销偏向锁,同时检查持有偏向锁的线程是否还在执行:
○ 第一个线程正在执行Synchronized方法(处于同步块),它还没有执行完,其他线程来抢夺,该偏向锁会被取消掉并出现锁升级,此时轻量级锁由原来持有偏向锁的线程持有,继续执行同步代码块,而正在竞争的线程会自动进入自旋等待获得该轻量级锁
○ 第一个线程执行完Synchronized(退出同步块),则将对象头设置为无所状态并撤销偏向锁,重新偏向。
Java15以后逐步废弃偏向锁,需要手动开启------->维护成本高
3、轻锁
概念:多线程竞争,但是任意时候最多只有一个线程竞争,即不存在锁竞争太激烈的情况,也就没有线程阻塞。
主要作用:有线程来参与锁的竞争,但是获取锁的冲突时间极短---------->本质是自旋锁CAS
轻量锁的获取:
自旋一定程度和次数(Java8 之后是自适应自旋锁------意味着自旋的次数不是固定不变的):
- 线程如果自旋成功了,那下次自旋的最大次数会增加,因为JVM认为既然上次成功了,那么这一次也大概率会成功
- 如果很少会自选成功,那么下次会减少自旋的次数甚至不自旋,避免CPU空转
轻量锁和偏向锁的区别:
- 争夺轻量锁失败时,自旋尝试抢占锁
- 轻量级锁每次退出同步块都需要释放锁,而偏向锁是在竞争发生时才释放锁
4、重量级锁状态
有大量线程参与锁的竞争,冲突性很高
五.小总结
锁升级的过程
- 锁升级后,hashcode去哪儿了?
● 各种锁优缺点、synchronized锁升级和实现原理
相关文章:

Java之Synchronized与锁升级
Synchronized与锁升级 一、概述 在多线程并发编程中 synchronized 一直是元老级角色,很多人都会称呼它为重量级锁。但是,随着 Java SE 1.6 对 synchronized 进行了各种优化之后,有些情况下它就并不那么重了。 本文详细介绍 Java SE 1.6 中为…...
kitex出现:open conf/test/conf.yaml: no such file or directory
open conf/test/conf.yaml: no such file or directory https://github.com/cloudwego/cwgo/issues/120 https://github.com/cloudwego/cwgo/issues/29 在使用Kitex生成的代码中,单元测试时回报错,如标题所示。出现该错的原因是,biz/servic…...
sql server多表查询
查询目标 现在有学生表和学生选课信息表,stu和stuSelect,stu中包含学生用户名、名字,stuSelect表中包含学生用户名,所选课程名 学生表: nameusername李明Li Ming李华Li Hua 学生选课表: usernameCourse…...

如何利用PPT绘图并导出清晰图片
在写论文的过程中,免不了需要绘图,但是visio等软件绘图没有在ppt上绘图比较熟练,尤其流程图结构图. 但是ppt导出的图片也不够清晰,默认分辨率是96dpi,而杂志投稿一般要求至300dpi。解决办法如下: 1.打开注…...

1.倒排索引 2.逻辑斯提回归算法
1.倒排索引 https://help.aliyun.com/zh/open-search/retrieval-engine-edition/introduction-to-inverted-indexes 倒排索引(Inverted Index)是一种数据结构,用于快速查找包含某个特定词或词语的文档。它主要用于全文搜索引擎等应用&#…...

Kafka消费者组
消费者总体工作流程 Consumer Group(CG):消费者组,由多个consumer组成。形成一个消费者组的条件,是所有消费者的groupid相同。 • 消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费…...

四. 基于环视Camera的BEV感知算法-BEVDepth
目录 前言0. 简述1. 算法动机&开创性思路2. 主体结构3. 损失函数4. 性能对比总结下载链接参考 前言 自动驾驶之心推出的《国内首个BVE感知全栈系列学习教程》,链接。记录下个人学习笔记,仅供自己参考 本次课程我们来学习下课程第四章——基于环视Cam…...
CentOS系统环境搭建(二十五)——使用docker compose安装mysql
centos系统环境搭建专栏🔗点击跳转 文章目录 使用docker compose安装mysqlMySQL81.新建文件夹2.创建docker-compose.yaml3.创建my.cnf4.mysql容器的启动和关闭 MySQL5.71.新建文件夹2.创建docker-compose.yaml3.创建my.cnf4.mysql容器的启动和关闭 使用docker comp…...

协作机器人(Collaborative-Robot)安全碰撞的速度与接触力
协作机器人(Collaborative-Robot)的安全碰撞速度和接触力是一个非常重要的安全指标。在设计和使用协作机器人时,必须确保其与人类或其他物体的碰撞不会对人员造成伤害。 对于协作机器人的安全碰撞速度,一般会设定一个上限值&…...

第11章 GUI Page400~402 步骤二 画直线
运行效果: 源代码: /**************************************************************** Name: wxMyPainterApp.h* Purpose: Defines Application Class* Author: yanzhenxi (3065598272qq.com)* Created: 2023-12-21* Copyright: yanzhen…...

华为gre隧道全部跑静态路由
最终实现: 1、pc1能用nat上网ping能pc3 2、pc1能通过gre访问pc2 3、全部用静态路由做,没有用ospf,如果要用ospf,那么两边除了路由器上跑ospf,核心交换机也得用ospf r2配置: acl number 3000 rule 5 deny…...

【c++】入门1
c关键字 命名空间 在C/C中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染ÿ…...

Python之Django项目的功能配置
1.创建Django项目 进入项目管理目录,比如:D盘 执行命令:diango-admin startproject demo1 创建项目 如果提示diango命令不存在,搜索diango-admin程序的位置,然后加入到环境变量path中。 进入项目,cd demo…...

P4 音频知识点——PCM音频原始数据
目录 前言 01 PCM音频原始数据 1.1 频率 1.2 振幅: 1.3 比特率 1.4 采样 1.5 量化 1.6 编码 02. PCM数据有以下重要的参数: 采样率: 采集深度 通道数 PCM比特率 PCM文件大小计算: …...
解决Electron中WebView加载部分HTTPS页面白屏的方法
Electron是一个开源的桌面应用程序框架,它允许使用Web技术构建跨平台的桌面应用。在Electron应用中,WebView 是一个常用的组件,用于嵌套加载Web内容。然而,有时候在加载使用 HTTPS 协议的页面时,可能会因为证书问题导致…...

【Java中创建对象的方式有哪些?】
✅Java中创建对象的方式有哪些? ✅使用New关键字✅使用反射机制✅使用clone方法✅使用反序列化✅使用方法句柄✅ 使用Unsafe分配内存 ✅使用New关键字 这是我们最常见的也是最简单的创建对象的方式,通过这种方式我们还可以调用任意的构造函数 (无参的和有…...
npm使用详解(好吧好吧是粗解)
目录 npm是什么? npm有什么用? npm安装 在 Windows 上 在 macOS 上 在 Linux 上(使用 apt 包管理器为例) 验证 npm 安装成功: npm使用 1. 初始化项目: 2. 安装和管理依赖: 3. 查看和…...

uniapp自定义头部导航怎么实现?
一、在pages.json文件里边写上自定义属性 "navigationStyle": "custom" 二、在对应的index页面写上以下: <view :style"{ height: headheight px, backgroundColor: #24B7FF, zIndex: 99, position: fixed, top: 0px, width: 100% …...

什么是 Dubbo?它有哪些核心功能?
文章目录 什么是 Dubbo?它有哪些核心功能? 什么是 Dubbo?它有哪些核心功能? Dubbo 是一款高性能、轻量级的开源 RPC 框架。由 10 层模式构成,整个分层依赖由上至下。 通过这张图我们也可以将 Dubbo 理解为三层模式&…...

(2021|CoRR,AugCLIP,优化)FuseDream:通过改进的 CLIP+GAN 空间优化实现免训练文本到图像生成
FuseDream: Training-Free Text-to-Image Generation with Improved CLIPGAN Space Optimization 公众:EDPJ(添加 VX:CV_EDPJ 或直接进 Q 交流群:922230617 获取资料) 目录 0. 摘要 1. 简介 2. CLIPGAN 文本到图…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...

群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...

Linux基础开发工具——vim工具
文章目录 vim工具什么是vimvim的多模式和使用vim的基础模式vim的三种基础模式三种模式的初步了解 常用模式的详细讲解插入模式命令模式模式转化光标的移动文本的编辑 底行模式替换模式视图模式总结 使用vim的小技巧vim的配置(了解) vim工具 本文章仍然是继续讲解Linux系统下的…...

如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...
uni-app学习笔记二十三--交互反馈showToast用法
showToast部分文档位于uniapp官网-->API-->界面:uni.showToast(OBJECT) | uni-app官网 uni.showToast(OBJECT) 用于显示消息提示框 OBJECT参数说明 参数类型必填说明平台差异说明titleString是提示的内容,长度与 icon 取值有关。iconString否图…...