当前位置: 首页 > news >正文

并发编程原理与实战(二十七)深入剖析synchronized底层基石ObjectMonitor与对象头Mark Word

上一篇文章中,我们深入分析了synchronized底层原理实现,其中分析了什么是对象的监视器‌,如何查看对象监视器‌的状态信息、显式隐式获取监视器锁等问题。本文继续深入分析synchronized底层原理实现,解决以下几个问题。

(1)synchronized以什么作为互斥?

(2)synchronized既然可重入,那么重入计数记录在哪里?

(3)持有监视器锁的线程信息记录在哪里?

(4)其他抢锁的等待者记录在哪里?

(5)java对象(锁对象)如何关联Monitor对象?

深入Monitor

Monitor实现并发协同实现原理

synchronized既然是一个独占锁,就像数据库的主键字段不能重复,同一个文件夹下文件名不能重复,那么它的互斥属性是怎么实现的?

synchronized基于Monitor实现,而Monitor是一种编程语言层面的高级的同步机制,用于在多线程环境下管理共享资源的访问,其核心机制包括互斥与同步两部分。Monitor的互斥属性通过内置的互斥锁(Mutex)实现,保证同一时刻仅有一个线程能进入Monitor内部(临界区)执行操作,其他线程需在等待队列中等待。前面我们已经知道,synchronized与wait/notify实现的并发协同是基于条件判断实现的,这个正是基于Monitor的条件变量实现线程间的协作,Monitor内部维护多个队列(如入口队列、条件队列)管理线程状态,确保并发协同的正确性。

互斥锁(Mutex,全称 ‌Mutual Exclusion‌)是操作系统层面用于控制多线程对共享资源访问的同步机制,其核心目标是确保同一时刻仅有一个线程能进入临界区。

综上所述,我们得出这个依赖关系:synchronized—>Monitor—>Mutex。

JVM ObjectMonitor结构体

synchronized是JVM层面上的锁,而JVM是c/c++写的,在JDK的源码中有对象监视器的结构体ObjectMonitor的定义,ObjectMonitor是HotSpot虚拟机中实现Java对象监视器的核心数据结构。在JDK的源码路径下:

jdk-master\jdk-master\src\hotspot\share\runtime\objectMonitor.cpp
jdk-master\jdk-master\src\hotspot\share\runtime\objectMonitor.hpp

我们摘取其中的构造函数进行分析:

ObjectMonitor::ObjectMonitor() {_header       = NULL;      // 存储关联对象的Mark Word初始值_count        = 0;         // 记录锁的重入次数_waiters      = 0;         // 等待线程计数器(如调用wait()的线程)_recursions   = 0;         // 锁的重入深度_object       = NULL;      // 关联的Java对象指针_owner        = NULL;      // 持有锁的线程指针_cxq          = NULL;      // 竞争锁失败的线程栈(FILO结构)_EntryList    = NULL;      // 阻塞线程的双向链表(锁分配缓冲区)_WaitSet      = NULL;      // 调用wait()的线程等待队列_WaitSetLock  = 0;         // 保护_WaitSet操作的锁_SpinFreq     = 0;         // 自旋锁优化参数_SpinClock    = 0;         // 自旋锁时间控制
}

如上所示,其构造函数初始化了以下关键字段:

1、基本状态字段‌

(1)header:存储锁对象的Mark Word原始值,初始为NULL。
(2)count:记录线程获取锁的次数,初始为0。
(3)waiters:等待线程计数器,初始为0。
(4)_recursions:锁重入次数,初始为0。

2、线程管理队列‌

(1)cxq:竞争锁失败的线程单向链表(FILO栈结构),初始为NULL。
(2)EntryList:阻塞线程的双向循环链表,作为锁分配的缓冲区,初始为NULL。
(3)_WaitSet:调用wait()的线程等待队列,初始为NULL。

3、所有权标识‌

(1)owner:指向持有锁的线程,初始为NULL。
(2)object:关联的Java对象指针,初始为NULL。

4、辅助控制字段‌

(1)WaitSetLock:保护WaitSet操作的锁,初始为0。
(2)SpinFreq和SpinClock:*自旋锁优化相关参数,初始为0。

该构造函数通过清零所有字段完成初始化,为后续锁竞争(如monitorenter指令)和线程同步(如wait/notify)提供基础支持。其设计体现了Monitor的互斥与同步机制,通过队列管理实现线程阻塞和唤醒。

对象头

结构体ObjectMonitor各个字段的定义解答了文章开头的第1、第2、第3、第4个问题,还剩“java对象(锁对象)如何关联Monitor对象?”这个问题没有解决。

我们注意到,结构体ObjectMonitor中的_header字段存储关联对象的Mark Word初始值。那么什么是Mark Word?要了解Mark Word得先了解java的对象头。

Java对象头是JVM实现对象内存管理、锁机制和GC的关键数据结构,其组成与锁状态动态关联。Java对象核心结构由以下几部分组成。

组成部分大小(64位JVM)功能描述
Mark Word8字节动态存储对象的运行时数据,包括哈希码(HashCode)、GC分代年龄、锁状态标志、偏向线程ID、锁指针等
Klass Pointer4/8字节指向方法区中对象的类元数据,用于确定对象所属类型
Array Length4字节(可选)仅数组对象存在,记录数组长度

Mark Word与锁优化

Mark Word部分存储着锁状态信息,而锁状态有无锁、偏向锁、轻量级锁、重量级锁这几种状态。所以对象头的Mark Word是synchronized锁机制的关键实现。锁状态的变化遵循‌不可逆‌的过程:‌无锁 → 偏向锁 → 轻量级锁 → 重量级锁。

(1)无锁状态。对象刚创建且未被任何线程加锁时处于无锁状态。锁标志位为01。

(2)偏向锁。首次有线程访问同步块时,升级为偏向锁;第一个线程访问同步块,且无竞争;消除单线程重复加锁的开销。锁标志位变为101。

(3)轻量级锁。第二个线程尝试获取锁(发生竞争),偏向锁撤销并升级为轻量级锁。锁标志位变为00。

(4)重量级锁。当CAS自旋失败(默认10次)或竞争加剧(如第三个线程加入),升级为重量级锁。对象头指向操作系统级互斥量(monitor),锁标志位变为10。

锁标志位状态转换示意流程:

无锁 (01) │↓ (首次单个线程访问)
偏向锁 (101) │↓ (多个线程访问,竞争发生)
轻量级锁 (00) │↓ (多个线程访问,CAS自旋失败或竞争加剧)
重量级锁 (10)

在《并发编程原理与实战(二十三)StampedLock应用实战与各种锁性能对比分析》这篇文章中,我们对比了包括synchronized在内的四种锁的性能,其中synchronized的表现最好。从上述锁标志位状态转换过程可以看出,根据竞争激烈程度自动调整锁策略,这其实是一个synchronized锁优化的结果。

线程执行synchronized(obj)时,JVM会检查obj对象的Mark Word锁标志位。当锁需要升级为重量级锁时,则创建或关联已有的ObjectMonitor,并将Mark Word更新为指向该Monitor的指针。通过这个过程实现java对象(锁对象)与Monitor对象的关联。

总结

本文深入分析了synchronized底层实现依赖,分析了JVM ObjectMonitor结构体的组成,最后分析了java对象头的Mark Word组成部分,简要分析了synchronized锁优化过程以及java对象(锁对象)与Monitor对象的关联的过程。synchronized的底层实现涉及的内容很多,就拿对象头‌Mark Word来讲,其组成部分就有很多项,我们的主要目的还是围绕问题来分析,在此我们不做更多深入的分析。


文章转载自:

http://mzgtxwMo.tzLfc.cn
http://NVPNdH85.tzLfc.cn
http://nbpsIF5K.tzLfc.cn
http://fM88Tlfe.tzLfc.cn
http://ihErszhH.tzLfc.cn
http://FcFWP6dC.tzLfc.cn
http://pbGVUoRp.tzLfc.cn
http://i9S1gumm.tzLfc.cn
http://ONCVMqzb.tzLfc.cn
http://eS95Q8o5.tzLfc.cn
http://or98vUW2.tzLfc.cn
http://LlDqAPkP.tzLfc.cn
http://sCupO2XJ.tzLfc.cn
http://OaHSqAoY.tzLfc.cn
http://TOYAcjlS.tzLfc.cn
http://P4c8YOvr.tzLfc.cn
http://nIGEI1Ip.tzLfc.cn
http://wXZ3LeOF.tzLfc.cn
http://icQVE33X.tzLfc.cn
http://bksGUbFI.tzLfc.cn
http://AlNyej23.tzLfc.cn
http://P9qbXsIH.tzLfc.cn
http://KZihvmI2.tzLfc.cn
http://FGLOVMVS.tzLfc.cn
http://1XO6GYtn.tzLfc.cn
http://UN8LP5Y7.tzLfc.cn
http://Njb6d0Iq.tzLfc.cn
http://TxC41ax6.tzLfc.cn
http://D1VgX0Ld.tzLfc.cn
http://DaVWs0Aw.tzLfc.cn
http://www.dtcms.com/a/373855.html

相关文章:

  • 国产化Word处理组件Spire.DOC教程:使用 Python 将 Markdown 转换为 HTML 的详细教程
  • CanMV K230 2025年度计划
  • 简单视频转换器 avi转mp4
  • 如何修改不同城市IP查询排名以增强广告投放效果
  • 04-Redis 启动与停止:服务管理全攻略(含命令行与图形化操作)
  • LangChain: Agent(代理)
  • 使用 BatchRendererGroup 创建渲染器
  • flutter鸿蒙:使用flutter_local_notifications实现本地通知
  • Redis中数据类型详解
  • CentOS 7安装最新nginx
  • 解决Win11 安全中心删掉存在隐患的工具
  • 二级缓存在实际项目中的应用
  • 第14篇:循环神经网络(RNN)与LSTM:序列建模的利器
  • 【P02_AI大模型之调用LLM的方式】
  • 浅谈Go 语言开发 AI Agent
  • pgsql for循环一个 数据文本 修改数据 文本如下 ‘40210178‘, ‘40210175‘, ‘40210227‘, ‘40210204‘
  • 工业检测机器视觉为啥非用工业相机?普通相机差在哪?
  • 基于MATLAB的粒子群算法优化广义回归神经网络的实现
  • 25年9月通信基础知识补充1:NTN-TDL信道建模matlab代码(satellite-communications toolbox学习)
  • Aider AI Coding项目 流式处理架构深度分析
  • 打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程的智慧工业开源了
  • PAT 1103 Integer Factorization
  • WindowManagerService (WMS)
  • Tool | AI类网址收录
  • SU-03T语音模块的使用
  • kubernetes-lxcfs解决资源可见性问题
  • 235kw发动机飞轮设计说明书CAD+设计说明书
  • Day9 | 类、对象与封装全解析
  • 【財運到】股票期货盯盘助手V3-盯盘界面找不到了
  • “微服务“一词总是出现,它是什么?