synchronized 锁优化原理

目录

一、轻量级锁

二、锁膨胀

三、自旋优化

四、偏向锁

五、锁消除


一、轻量级锁

1. 会创建一个锁记录 Lock Record(保存在线程栈中),尝试 CAS 修改 Mark Word 中的对象头,是一种乐观锁的思想,而不是将 Java 对象与操作系统的 Monitor 对象关联起来(重量级锁)

2. CAS 修改

  • mark word 最后两位是 01 表示无锁,最后两位是 00 表示有锁,加锁失败
  • 也有可能是锁的重入(判断对象头的锁记录地址是不是自己),如果是自己执行锁重入,那么再添加一条锁记录 Lock Record 作为锁重入的记录(重入时,锁记录为 null;非重入锁,锁记录是对象头 Mark Word 的值),解锁的时候就是解一次锁去掉一条锁记录,要等栈帧中的所有锁记录被去除才是真的释放了锁

二、锁膨胀

如果在尝试 CAS 替换锁对象的对象头 Mark Word 和锁记录时,替换失败,有种情况就是其他线程对该对象加上了轻量级锁(有竞争),此时就会发生锁膨胀,将轻量级锁升级为重量级锁

  • 加轻量级锁失败,申请 Monitor 锁,让 Object 指向 Monitor,建立 Java 对象和 Monitor 的关联,将 mark word 最后两位变为 10,表示重量级锁
  • 加锁(轻量级锁)失败的线程,加入 Monitor 的阻塞队列
  • 解锁:Thread-0(获取轻量级锁成功的)通过 CAS 将 Mark Word 的值恢复给对象头,由于对象头已经变成了 Monitor 的地址,所以恢复失败,接下来就会走重量级锁的解锁流程,先到 Monitor 中清空 Owner,到阻塞队列唤醒 Thread-1,恢复 Mark Word

三、自旋优化

重量级锁竞争时,线程可以通过自旋来优化,原来需要加入阻塞队列(上下文切换),开销较大,自旋可以重试访问重新判断锁是否可用,自旋成功(在自旋过程中,持有锁的线程退出了同步代码块,释放锁)避免阻塞

  • 自旋会一直消耗 CPU,适用于多核的情况(单核在执行持有锁的线程的任务,自旋线程会被阻塞,没有意义)
  • 问题:可以间隔一段时间再重试吗❓
  • 自旋失败:重试一定次数还是失败就加入阻塞队列
  • Java 6 之后 的自旋锁是自适应的,JVM 底层会根据最近自旋是否成功来判断自旋的次数,最近成功了说明成功概率比较大,就多自旋几次;最近失败了就少重试几次或者不重试;Java 7 之后不能控制是否开启自旋锁,都是由底层来控制是否开启的

四、偏向锁 ‼️

轻量级锁在没有发生竞争时,同一个线程需要重入锁,也需要进行多次 CAS 操作,影响性能

Java 6 中引入的偏向锁来做进一步优化:第一次进行 CAS 操作时,将线程 ID 设置到对象头 Mark Word 中,之后需要重入锁,只需要判断对象头的 ID 是不是自己,不需要进行 CAS 操作

之后只要不发生竞争,这个对象就归该线程所有(问题:如何判断是否发生锁竞争❓锁竞争会将轻量级锁升级为重量级锁,对象头的 Mark Word 会指向 Monitor,释放锁的时候 CAS 失败)

1. 创建对象:如果开启了偏向锁(默认开启),mark word 最后三位是 101,此时 thread、epoch、age 都是 0,但是偏向锁默认是有延迟的,在程序刚启动时不会生效,如果想避免延迟立即生效可以加 JVM 参数;没有开启偏向锁,mark word 最后三位是 001,此时 hashcode、age 都是 0,在第一次用到 hashcode 时才会赋值

问题:创建对象时 101 的意思是已经加了偏向锁还是表示可以加偏向锁❓

2. 加锁:给对象头设置线程 id

3. 释放锁:线程 id 还是保持上一个线程,除非发生锁竞争,这就是偏向锁名字的来由,偏向同一个线程,当该线程再次请求时就不需要再进行复杂的加锁操作

4. 适用场景:单线程多次使用,不适合有多个线程来竞争这把锁的场景,如果知道可能会有冲突,可以通过加 JVM 参数禁用偏向锁,让对象刚创建时是 Normal 正常状态,加锁时后两位是 00(轻量级锁)

5. 加锁顺序:偏向锁 -> 轻量级锁 -> 重量级锁

6. 撤销

  • ⚠️ 注意:调用了 hashcode() 之后就会禁用 / 撤销偏向锁,因为调用 hashcode() 会给 mark word 赋值哈希码,mark word 的大小不够存放线程 id 了,本来可偏向的状态就会被撤销,由 101 变成 001;但是轻量级锁和重量级锁不会有这个问题,因为轻量级锁调用 hashcode() 之后会将哈希码存到锁记录里,重量级锁会存到 monitor 里,解锁时还会还原
  • 释放锁之后其他线程要使用锁:撤销偏向状态(101 -> 001,由可偏向改为不可偏向),将偏向锁升级为轻量级锁(注意不是锁竞争,如果冲突的话是升级为重量级锁了)
  • 调用 wait/notify:也会撤销偏向锁升级为重量级锁,因为 wait/notify 只有重量级锁有

撤销偏向状态比较影响性能:虽然对象被多个线程访问,但并没有发生竞争(t1 释放时候 t2 才访问的),撤销偏向状态升级锁没啥必要,可以将原本偏向 t1 的置为偏向 t2 的(修改 mark word 的线程 id)⬇️

7. 批量重偏向:被多个线程访问但没有发生竞争,修改偏向的线程 id

  • 开启批量重偏向的阈值:当撤销偏向锁的次数达到 20 次之后,JVM 会觉得可能偏向错了,就在加锁时重新偏向加锁线程(第二十次就已经改了)
  • 批量:修改偏向状态是将这批对象的线程 id 都修改成最后一个请求他们锁的线程

8. 批量撤销:撤销偏向达到 40 次(本来就不该偏向),JVM 会对该类的所有对象批量撤销偏向锁,直接进入轻量级锁的状态,新建对象也是 001 不可偏向的

五、锁消除

JVM 的高级优化技术,允许编译器确定锁对象不会引起线程安全问题时,减少不必要的加锁操作,提升程序性能,是 JVM 自动进行的优化,对开发者是透明的

1. JIT 即时编译器会对热点代码(重复调用的)进行优化,如果 synchronized 的对象不会逃离方法的作用范围(局部变量),就可以不加锁,直接消除锁

2. 工作原理

3. 示例场景

  • 如果一个方法内部创建了一个局部变量,对该局部变量进行加锁操作,但是该变量仅存在当前方法栈帧中,不会被其他线程并发访问,所以没有线程安全问题,JVM 就会进行锁消除的优化

4. 优点

  • 性能提升:减少上下文切换和同步开销
  • 简化编程:开发者不需要自己去判断哪些锁是可以避免的,由编译器来自动执行

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

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

相关文章

机器学习——强化学习状态值函数V和动作值函数Q的个人思考

最近在回顾《西瓜书》的理论知识,回顾到最后一章——“强化学习”时对于值函数部分有些懵了,所以重新在网上查了一下,发现之前理解的,包括网上的大多数对于值函数的描述都过于学术化、公式化,不太能直观的理解值函数以…

macos Automator自动操作 app, 创建自定义 应用程序 app 的方法

mac内置的这个 自动操作 automator 应用程序,可以帮助我们做很多的重复的工作,可以创建工作流, 可以录制并回放操作, 还可以帮助我们创建自定的应用程序,下面我们就以创建一个自定义启动参数的chrome.app为例&#xff…

cube-studio 开源一站式云原生机器学习/深度学习/大模型训练推理平台介绍

全栈工程师开发手册 (作者:栾鹏) 一站式云原生机器学习平台 前言 开源地址:https://github.com/tencentmusic/cube-studio cube studio 腾讯开源的国内最热门的一站式机器学习mlops/大模型训练平台,支持多租户&…

绘图黑系配色

随便看了几篇小论文,里面的黑配色挺喜欢的,虽然平时SCI系配色用的多,但看到纯黑配色与黑加蓝配色,那就是我最心上的最优style。

一文了解IP地址冲突的起因与解决方案

IP 地址冲突是困扰网络管理员影响网络的正常运行的常见因素。深入理解并有效解决 IP 地址冲突故障对于维护网络的高效稳定运行具有重要意义。 一、IP 地址冲突的原因 (一)人为配置错误 网络用户在手动配置 IP 地址时,对网络配置了解不多用户…

OpenGL3.3_C++_Windows(23)

伽ga马校正 物理亮度 光子数量 线性空间:光子数(亮度)和颜色值的线性关系人眼感知的亮度:对比较暗的颜色变化更敏感,感知亮度基于人的感觉非线性空间:光子数(亮度)和 颜色值^2.2,恰好符合屏幕…

为什么我学个 JAVA 就已经耗尽所有而有些人还能同时学习多门语言

在开始前刚好我有一些资料,是我根据网友给的问题精心整理了一份「JAVA的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!!我的入门语言是C&#xff0c…

互联网场景下人脸服务基线方案总结

1.简介 1.1目的 在过去的一段时间里,因为听见业务对人脸服务方案的需求,针对网络视频中关键人物定位的检索任务,完成了基于互联网场景的人脸基线服务的构建。本文档是对当前基线服务以后之后解决方案的优化进行总结。 1.2范围 本文档描述的人…

c++读取文件时出现中文乱码

原因:UTF-8格式不支持汉字编码 解决:改成ANSI,因为ANSI编码支持汉字编码

Python学习路线图(2024最新版)

这是我最开始学Python时的一套学习路线,从入门到上手。(不敢说精通,哈哈~) 一、Python基础知识、变量、数据类型 二、Python条件结构、循环结构 三、Python函数 四、字符串 五、列表与元组 六、字典与集合 最后再送给大家一套免费…

KVB App:中国制造业数据支撑澳元,分析师预计挑战0.6750阻力

摘要: 中国6月财新制造业PMI上升至51.8,反映出制造业生产经营活动的持续扩张。这一数据不仅高于预期,还为澳元提供了强有力的支撑。技术分析显示,澳元/美元可能会在短期内挑战0.6750阻力水平。 中国制造业数据解析 6月&#xff0…

python异常、模块与包

目录 了解异常异常的捕获方法python模块python包安装第三方包 了解异常 什么是异常 当检测到一个错误时,python解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的“异常”,也就是我们常说的BUG bug单词的诞生 早期…

Python tkinter: 开发一个目标检测GUI小程序

程序提供了一个用户友好的界面,允许用户选择图片或文件夹,使用行人检测模型进行处理,并在GUI中显示检测结果。用户可以通过点击画布上的检测结果来获取更多信息,并使用键盘快捷键来浏览不同的图片。 一. 基本功能介绍 界面布局&am…

智芯开发板----时钟的使用

一、开发板时钟概述 介绍 Z20K11xM 的时钟结构,分布以及各个外设时钟源的选择。SCC 模块用于选择系统时钟,产生 core clock、bus clock 和 flash clock,分 别用于驱动 core 及高速外设、普通外设和 flash。PARCC 模块用于单独设置 每个外设的…

中霖教育怎么样?税务师通过率高吗?

中霖教育怎么样?税务师通过率高吗? 我们在税务师考试培训方面有着不错的成绩,这都是老师与学员共同努力的结果。 采用小班教学模式,确保每位学员都能得到足够的关注和指导,在学习过程中针对学员的薄弱环节进行专项突破。 因为大部分学员…

【C语言】常见的字符串函数

©作者:末央& ©系列:C语言初阶(适合小白入门) ©说明:以凡人之笔墨,书写未来之大梦 目录 strlen函数模拟实现 strstr子串查找函数模拟实现 strtok字符串分割 strlen函数 strlen函数是一个用于求字符串长度的库函数。它的参数是被求长度的字…

【爱上C++】详解string类2:模拟实现、深浅拷贝

在上一篇文章中我们介绍了string类的基本使用,本篇文章我们将讲解string类一些常用的模拟实现,其中有很多细小的知识点值得我们深入学习。Let’s go! 文章目录 类声明默认成员函数构造函数析构函数拷贝构造函数深浅拷贝问题传统写法现代写法…

泛型的使用(<T>)

文章目录 前言一、泛型是什么?二、泛型的使用 1.定义泛型类2.泛型的常规用法总结 前言 强制类型转换存在一定隐患,如数据丢失、内存溢出、运行时错误、程序逻辑错误等。所以提供了泛型机制,使程序员可以定义安全的数据类型进行操作。通俗的理…

CEPH 系统盘挂了,如何使用数据盘恢复

硬盘损坏是早晚的时,CEHP数据盘坏了,使用CEPH的基本都轻车熟路了,如果系统盘坏了呢?不知道的可能会采取整个系统盘全做的方式 前提条件:使用cephadm搭建集群 如果换服务器,请确保CEPH数据盘放到其它服务器上…

Python基础教程——一次搞懂 Python 字典!Python字典的20种神奇用法

Python 字典(Dictionary)是数据结构中的一种重要类型。它以键值对的形式存储数据,具有快速查找的特性。今天我们将通过生动有趣的案例,来探讨字典的20个经典操作,帮助大家深入理解和掌握这些概念。 1. 创建字典 字典…