深入理解Synchronized

news2025/1/16 20:56:50

Synchronized 底层原理

  • Synchronized的语义底层是通过一个 Monitor 的对象来完成,其实wait/notify等方法也依赖于 Monitor 对象,这就是为什么只有在同步的块中,拿到锁之后,才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
  • 下面会具体讲解 Monitor 是什么

Java 对象头

  • 以32位虚拟机为例:

  • 普通对象:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GFb19ABJ-1673008401334)(深入理解Synchronized.assets/20210130134419113.png)]

  • 数组对象:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0GMbs2b8-1673008401335)(深入理解Synchronized.assets/20210130134453728.png)]

  • 其中 Mark Word 的结构,如下图所示:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DGq86KDh-1673008401335)(深入理解Synchronized.assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDI4MDU3Ng==,size_16,color_FFFFFF,t_70.png)]

  • 所以一个 Java 对象的结构如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z2Jsi7gL-1673008401336)(深入理解Synchronized.assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDI4MDU3Ng==,size_16,color_FFFFFF,t_70-16729984581845.png)]

Monitor

  • Monitor 被翻译为监视器或者说管程
  • 每个 java 对象都可以关联一个 Monitor ,如果使用 synchronized 给对象上锁(重量级),该 Java 对象头的 Mark Word 中就被设置为指向 Monitor 对象的指针。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h7K0iZ1V-1673008401336)(深入理解Synchronized.assets/20200608144917.png)]

  • 当线程执行到临界区代码时,如果使用了synchronized,会先查询synchronized中所指定的对象(obj)是否绑定了Monitor
    • 如果没有绑定,则会先去与Monitor绑定,并且将Owner设为当前线程。
    • 如果已经绑定,则会去查询该Monitor是否已经有了Owner
      • 如果没有,则Owner与将当前线程绑定
      • 如果有,则放入EntryList,进入阻塞状态(blocked)
  • 当Monitor的Owner将临界区中代码执行完毕后,Owner便会被清空,此时EntryList中处于阻塞状态的线程会被叫醒并竞争,此时的竞争是非公平的。什么是非公平呢?非公平就是无法保证先来的线程优先获取锁公平锁:公平锁就是保障了多线程下各线程获取锁的顺序,先到的线程优先获取锁。

Synchronized 的优化(jdk 6)

轻量级锁

  • 调用 synchronized 去加锁的时候,会优先使用轻量级锁去加锁,如果失败了,才会使用重量级锁去加锁

  • 轻量级锁的使用场景是:如果一个对象虽然有多个线程要对它进行加锁,但是加锁的时间是错开的(也就是没有线程竞争),那么可以使用轻量级锁来进行优化。轻量级锁对使用者是透明的,即语法仍然是 synchronized

  • 具体加锁过程:

    • 每次运行到 synchronized 代码块时,都会在线程的栈中创建锁记录(Lock Record)对象,每个线程都会包括一个锁记录的结构,锁记录内部可以储存对象的 Mark Word 和对象引用 reference

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eY8vh0XL-1673008401337)(深入理解Synchronized.assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDI4MDU3Ng==,size_16,color_FFFFFF,t_70-16730063937947.png)]

    • 让锁记录中的 Object reference 指向对象,并且尝试用 cas(compare and swap) 替换 Object 对象的 Mark Word ,将 Mark Word 的值存入锁记录中。

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0fsbJ6sn-1673008401337)(深入理解Synchronized.assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDI4MDU3Ng==,size_16,color_FFFFFF,t_70-16730064153079.png)]

    • 如果 cas 替换成功,那么对象的对象头储存的就是锁记录的地址和 State状态 00 表示轻量级锁,如下所示

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VFKzL6jm-1673008401337)(深入理解Synchronized.assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDI4MDU3Ng==,size_16,color_FFFFFF,t_70-167300644500411.png)]

    • 如果cas失败,有两种情况

    • 1.如果是其它线程已经持有了该 Object 的轻量级锁,那么表示有竞争,首先会进行自旋锁,自旋一定次数后,如果还是失败就进入锁膨胀阶段。

    • 2.如果是自己的线程已经执行了 synchronized 进行加锁,那么再添加一条 Lock Record 作为重入的计数。

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eL7aeQ9m-1673008401337)(深入理解Synchronized.assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDI4MDU3Ng==,size_16,color_FFFFFF,t_70-167300652966613.png)]

    • 当线程退出 synchronized 代码块的时候,如果获取的是取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减一

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-llEjw2rZ-1673008401338)(深入理解Synchronized.assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDI4MDU3Ng==,size_16,color_FFFFFF,t_70-167300655065415.png)]

    • 当线程退出 synchronized 代码块的时候,如果获取的锁记录取值不为 null,那么使用 cas 将 Mark Word 的值恢复给对象

      • 成功则解锁成功
      • 失败,则说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程

锁膨胀

  • 如果在尝试加轻量级锁的过程中,cas 操作无法成功,这是有一种情况就是其它线程已经为这个对象加上了轻量级锁,这时就要进行锁膨胀,将轻量级锁变成重量级锁。

  • 当 Thread-1 进行轻量级加锁时,Thread-0 已经对该对象加了轻量级锁

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lF5ZFdwX-1673008401338)(深入理解Synchronized.assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDI4MDU3Ng==,size_16,color_FFFFFF,t_70-167300669045317.png)]

  • 这时 Thread-1 加轻量级锁失败,进入锁膨胀流程

    • 即为对象申请Monitor锁,让Object指向重量级锁地址

    • 然后自己进入Monitor 的EntryList 变成BLOCKED状态

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KukeDXCZ-1673008401338)(深入理解Synchronized.assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDI4MDU3Ng==,size_16,color_FFFFFF,t_70-167300673196419.png)]

  • 当 Thread-0 退出 synchronized 同步块时,使用 cas 将 Mark Word 的值恢复给对象头,对象的对象头指向 Monitor,那么会进入重量级锁的解锁过程,即按照 Monitor 的地址找到 Monitor 对象,将 Owner 设置为 null ,唤醒 EntryList 中的 Thread-1 线程

自旋优化

  • 重量级锁竞争的时候,没争抢到锁的线程不立即进入 EntryList 阻塞,还可以使用自旋来进行优化,如果当前线程自旋成功(即在自旋的时候持锁的线程释放了锁),那么当前线程就可以不用进行上下文切换就获得了锁
  • 自旋重试成功的情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8ZDc0zZr-1673008401339)(深入理解Synchronized.assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDI4MDU3Ng==,size_16,color_FFFFFF,t_70-167300697122921.png)]

  • 自旋重试失败的情况,自旋了一定次数还是没有等到持锁的线程释放锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d98gkRJ0-1673008401339)(深入理解Synchronized.assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDI4MDU3Ng==,size_16,color_FFFFFF,t_70-167300698508823.png)]

  • 自旋会占用 CPU 时间,单核 CPU 自旋就是浪费,多核 CPU 自旋才能发挥优势。在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋,总之,比较智能Java 7 之后不能控制是否开启自旋功能

偏向锁

  • 在轻量级锁中,我们可以发现,如果同一个线程对同一个对象进行重入锁时,也需要执行 CAS 操作,这是有点耗时滴,那么 java6 开始引入了偏向锁,只有第一次使用 CAS 时将对象的 Mark Word 头设置为偏向线程 ID,之后这个入锁线程再进行重入锁时,发现线程 ID 是自己的,那么就不用再进行CAS了
  • 分析代码,比较轻量级锁与偏向锁
static final Object obj = new Object();
public static void m1() {
	synchronized(obj) {
		// 同步块 A
		m2();
	}
}
public static void m2() {
	synchronized(obj) {
		// 同步块 B
		m3();
	}
}
public static void m3() {
	synchronized(obj) {
		// 同步块 C
	}
}
  • 分析如图:

**[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WVOmmzpP-1673008401339)(深入理解Synchronized.assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDI4MDU3Ng==,size_16,color_FFFFFF,t_70-167300719539725.png)]

  • 偏向锁的对象头格式如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6aT9Lp2I-1673008401339)(深入理解Synchronized.assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDI4MDU3Ng==,size_16,color_FFFFFF,t_70-167300725221627.png)]

  • 一个对象的创建过程
    • 如果开启了偏向锁(默认是开启的),那么对象刚创建之后,Mark Word 最后三位的值101,并且这是它的 Thread,epoch,age 都是 0 ,在加锁的时候进行设置这些的值.
    • 偏向锁默认是延迟的,不会在程序启动的时候立刻生效,如果想避免延迟,可以添加虚拟机参数来禁用延迟:-XX:BiasedLockingStartupDelay=0 来禁用延迟
    • 注意:处于偏向锁的对象解锁后,线程 id 仍存储于对象头中

撤销偏向锁

  • 以下几种情况会使对象的偏向锁失效

    • 调用对象的 hashCode 方法

    • 其他线程使用该对象,会将偏向锁升级为轻量级锁

    • 调用了 wait/notify 方法(调用wait方法会导致锁膨胀而使用重量级锁

批量重偏向

  • 如果对象虽然被多个线程访问,但是线程间不存在竞争,这时偏向 t1 的对象仍有机会重新偏向 t2
    • 重偏向会重置Thread ID
  • 当撤销超过20次后(超过阈值),JVM 会觉得是不是偏向错了,这时会在给对象加锁时,重新偏向至加锁线程。

批量撤销

  • 当撤销偏向锁的阈值超过 40 以后,就会将整个类的对象都改为不可偏向的

锁撤销

  • JIT 即时编译器,会进行优化

d ID

  • 当撤销超过20次后(超过阈值),JVM 会觉得是不是偏向错了,这时会在给对象加锁时,重新偏向至加锁线程。

批量撤销

  • 当撤销偏向锁的阈值超过 40 以后,就会将整个类的对象都改为不可偏向的

锁撤销

  • JIT 即时编译器,会进行优化

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

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

相关文章

AI助力产品质量检验,基于YOLO实现瓷砖缺陷问题检测识别

在我之前的文章中也写过很多关于生产质检相关的实践文章,一直觉得这块是比较有意思的应用方向,做出来的模型能够以一种更加直观贴切的形式展现出来,瓷砖缺陷问题检测识别也是一个比较老的话题了,今天还是想拿出来具体实践做一下&a…

Golang.org/x库初探1——image库

Golang有一个很有意思的官方库,叫golang.org/x,x可能是extends,experimental,总之是一些在官方库中没有,但是又很有用的库。最近花点时间把这里有用的介绍一下。 Image库 提供更多的图像格式 golang.org/x/image库整…

Linux 网络驱动

1. linux 里面驱动三巨头:字符设备驱动、块设备驱动、网络设备驱动。2.嵌入式网络硬件分为两部分: MAC 和 PHY。如果一款芯片数据手册说自己支持网络,一般都是说的这款 SOC 内置 MAC, MAC 类似 I2C 控制器、SPI 控制器一样的外设。…

Java三大技术平台是什么?

为了使软件开发人员、服务提供商和设备生产商可以针对特定的市场进行开发,SUN公司将Java划分为三个技术平台,它们分别是 JavaSE、 JavaEE和 JavaME。Java SE( Java Platform Standard Edition)标准版,是为开发普通桌面和商务应用程序提供的解…

零宽断言正则表达式替换方案

一、背景 safari浏览器不支持零宽断言正则表达式 二、解决方案 使用其他正则替换零宽断言正则&#xff08;包含&#xff1a;(?<)正向肯定预查、(?<!)正向否定预查、(?)反向肯定预查、(?!)反向否定预查&#xff09; 三、涉及场景 1、仅校验&#xff0c;不取值 如表…

首汽约车驶向极速统一之路!出行平台如何基于StarRocks构建实时数仓?

作者&#xff1a;王满&#xff0c;高级数据架构工程师首汽约车&#xff08;以下简称 “首约”&#xff09;是首汽集团为响应交通运输部号召&#xff0c;积极拥抱互联网&#xff0c;推动传统出租车行业转型升级&#xff0c;加强建设交通强国而打造的网约车出行平台。 在用车服务…

KernelSU: 内核 ROOT 方案, KernelSU KernelSU KernelSU 新的隐藏root防止检测 封号方案

大约一年多以前&#xff0c;我在一篇讲Android 上 ROOT 的过去、现在和未来https://mp.weixin.qq.com/s?__bizMjM5Njg5ODU2NA&mid2257499009&idx1&sn3cfce1ea7deb6e0e4f2ac170cffd7cc1&scene21#wechat_redirect 的文章中提到&#xff1a; 我认为&#xff0c;随…

三菱FX5U 多个表格运行指令 DRVTBL

简述该指令可以用GX Works3预先在表格数据中设定的控制方式的动作&#xff0c;&#xff08;连续或步进&#xff09; 执行多行。 本文演示了步进执行多行。指令解释2.1梯形图中的指令第一个参数&#xff1a;输出脉冲的轴编号 &#xff0c;K1,K2,K3,K4... 第二个参数&#xff1a;…

ESP8266 Windows开发环境搭建(IDE1.5)好用不骗人

最近一个项目需要用ESP8266&#xff0c;找了很多文章进行环境搭建编译都很问题&#xff0c;不是make Menuconfig 不出来&#xff0c;就是编译报错&#xff0c;现总结如下。 我在自己电脑上没弄出来&#xff0c;就安装了一个虚拟机很干净的环境没有其它开发环境影响。 提前去官…

逆向入门|全国建筑市场监管公共服务平台JS逆向

看了志远的公开课&#xff0c;自己做一下练手。 全国建筑市场监管公共服务平台&#xff08;四库一平台&#xff09; 先点到 数据这里打开f12看一眼 第一个就是 https://jzsc.mohurd.gov.cn/api/webApi/dataservice/query/comp/list?pg1&pgsz15&total450 取这个地址…

线段树讲解

0、引入 假设给定一个长度为 1001 的数组&#xff0c;即下标 0 到 1000。 现在需要完成 3 个功能&#xff1a; add(1, 200, 6); //给下标 1 到 200 的每个数都加 6&#xff1b; update(7, 375, 4); //下标 7 到 375 的数全部修改为 4 query(3, 999); //下标 3 到 999 所有数…

深入理解如何利用PWM驱动舵机:ESP32驱动DS1115舵机

深入理解如何利用PWM驱动舵机&#xff1a;ESP32驱动DS1115舵机DS1115舵机技术规格举例说明之前做了一个项目&#xff0c;关于ESP32驱动DS1115舵机&#xff0c;但是在项目运行的过程中由于学艺不精&#xff0c;导致电机抽搐 &#x1f635;‍&#x1f4ab;&#xff0c;所以特意拜…

声纹识别可靠评测

分享嘉宾 | 李蓝天 文稿整理 | William 1 Introduction 声纹识别的发展&#xff0c;非常迅猛&#xff0c;在一些基准上取得了不错的效果&#xff0c;但如果将其部署到一个实际的应用系统里面&#xff0c; 从应用方的反馈来看&#xff0c;纹识别在很多场景里的鲁棒性并不理想。…

聚观早报 | 亚马逊将裁员17000人;苹果砍单MacBook等产品线架构

今日要闻&#xff1a;亚马逊将裁员17000人&#xff1b;苹果砍单MacBook等产品线&#xff1b;京东科技调整组织架构&#xff1b;小米x徕卡团队获技术大奖&#xff1b;必应搜索或将纳入ChatGPT亚马逊将裁员17000人 1 月 5 日消息&#xff0c;知情人士称&#xff0c;亚马逊新一轮裁…

正版授权|FastStone Capture 专业屏幕截图录屏工具软件 商业版,支持商业用途。

现在截图对每个人来说都是一个必不可少的功能。QQ软件截图、360游览器截图等都是相对简单快速的途径。但是如果你对截图有更多的要求&#xff0c;那么这里推荐一款截图软件&#xff0c;它就是FastStone Capture。这个对于商城老用户来说&#xff0c;几乎是接近人手一份。强大的…

【VUE3】保姆级基础讲解(六)Axios库

目录 Axios介绍与原生的差异 发送常见的请求和配置选项 1、发送request请求 baseURL &#xff1a; 2、发送get请求 3、发送post请求 axios.all Axios创建新的实例 请求和响应拦截 请求拦截 响应拦截 Axios介绍与原生的差异 Axios其实就是一个网络请求库 与原生的差异&…

勇夺中国市场豪华品牌第一名后,特斯拉S3XY全系售价调整

比你优秀的人比你更努力&#xff0c;用这句话形容特斯拉最贴切不过。 刚刚过去的2022年&#xff0c;特斯拉在海内外市场交出了亮眼答卷&#xff1a;全球共计交付产品超131万辆&#xff0c;同比增长40%&#xff1b;乘联会给出的数据显示&#xff0c;上海超级工厂全年交付71.1万辆…

不止IVAS,微软Azure也在布局这些军事模拟场景

一提起微软在军事领域的应用&#xff0c;我们第一印象可能是美军以220亿美元采购HoloLens 2 AR头显的项目&#xff0c;这个项目后期由于AR光学和设计方面受限&#xff0c;正式应用的日期一直再推迟。实际上&#xff0c;微软除了向美军提供HoloLens外&#xff0c;还提供了基于云…

Unity 3D GUI 简介||OnGUI Button 控件

游戏开发过程中&#xff0c;开发人员往往会通过制作大量的图形用户界面&#xff08; Graphical User Interface&#xff0c;GUI &#xff09;来增强游戏与玩家的交互性。 Unity 3D 中的图形系统分为 OnGUI、NGUI、UGUI等&#xff0c;这些类型的图形系统内容十分丰富&#xff0…

第05章 数组、排序和查找

数组 基本介绍 数组可以存放多个同一类型的数据&#xff0c;数组也是一种数据类型&#xff0c;是引用类型。 即&#xff1a;数组就是一组数据。 数组的使用 1、数组的定义 方法一&#xff1a; 数据类型[] 数组名 new 数据类型[大小] 说明&#xff1a;int[] a new int[5…