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

synchronized 锁升级机制详解

synchronized 锁升级机制详解

Java中的synchronized锁会经历一个从无锁到偏向锁,再到轻量级锁,最后到重量级锁的升级过程,这种优化称为锁升级锁膨胀。以下是完整的锁升级流程和原理分析:

一、锁状态总览

锁状态存储内容适用场景优缺点
无锁对象正常状态单线程访问无竞争开销
偏向锁线程ID + Epoch(时间戳)同一线程重复访问加解锁几乎无代价
轻量级锁指向栈中锁记录的指针多线程交替访问自旋消耗CPU
重量级锁指向互斥量(mutex)的指针多线程竞争激烈线程阻塞唤醒开销大

二、锁升级完整流程

第一个线程访问
出现第二个线程
自旋超过阈值/第三个线程
批量重偏向
批量撤销
无锁
偏向锁
轻量级锁
重量级锁
重置偏向锁

三、各阶段详细原理

1. 偏向锁(Biased Locking)

对象头结构

|-------------------------------------------------------|--------------------|
|                  Mark Word (64 bits)                  |       State        |
|-------------------------------------------------------|--------------------|
| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | 01 |
|-------------------------------------------------------|--------------------|
|  thread:54 |       epoch:2       | unused:1 | age:4 | biased_lock:1 | 01 |
|-------------------------------------------------------|--------------------|

工作流程

  1. 第一个线程访问时,通过CAS将线程ID写入对象头
  2. 该线程再次进入时只需检查线程ID是否匹配
  3. 出现竞争时(另一个线程尝试获取锁),撤销偏向锁

JVM参数

-XX:+UseBiasedLocking  # 启用偏向锁(JDK15后默认禁用)
-XX:BiasedLockingStartupDelay=4000  # 延迟启用时间(ms)

2. 轻量级锁(Lightweight Lock)

加锁过程

  1. 在当前线程栈帧中创建锁记录(Lock Record)
  2. 拷贝对象头Mark Word到锁记录(Displaced Mark Word)
  3. 用CAS将对象头替换为指向锁记录的指针
  4. 成功则获取锁,失败则自旋或升级

对象头变化

|-------------------------------------------------------|--------------------|
|               Pointer to Lock Record                  |       State        |
|-------------------------------------------------------|--------------------|
|              62 bits                 |      00        |       lightweight |
|-------------------------------------------------------|--------------------|

3. 重量级锁(Heavyweight Lock)

实现原理

  • 依赖操作系统互斥量(mutex)实现
  • 未获取锁的线程进入阻塞状态
  • 通过监视器(Monitor)管理,涉及用户态到内核态的切换

对象头结构

|-------------------------------------------------------|--------------------|
|               Pointer to Heavyweight Monitor          |       State        |
|-------------------------------------------------------|--------------------|
|              62 bits                 |      10        |       heavyweight |
|-------------------------------------------------------|--------------------|

四、关键升级触发条件

升级路径触发条件
无锁 → 偏向锁第一个线程访问同步块
偏向锁 → 轻量级锁第二个线程尝试获取锁(发生竞争)
轻量级锁 → 重量级锁自旋超过阈值(-XX:PreBlockSpin,JDK6后自适应自旋)或第三个线程参与竞争

五、特殊处理机制

1. 批量重偏向(Bulk Rebias)

场景:当一类对象的偏向锁被多个线程访问,但未真正竞争时
行为:JVM会批量修改这些对象的偏向线程ID(epoch值递增)
阈值:默认20次(-XX:BiasedLockingBulkRebiasThreshold

2. 批量撤销(Bulk Revoke)

场景:当一类对象的偏向锁总是发生竞争
行为:JVM会禁用该类的偏向锁功能
阈值:默认40次(-XX:BiasedLockingBulkRevokeThreshold

六、锁升级性能影响

锁状态适用场景性能特点
偏向锁单线程重复访问加解锁几乎无开销(仅1次CAS)
轻量级锁多线程交替访问自旋消耗CPU,但避免线程阻塞
重量级锁高并发竞争线程阻塞/唤醒开销大(微秒级)

七、实战建议

  1. 偏向锁优化

    # 对明确存在竞争的类禁用偏向锁
    -XX:-UseBiasedLocking
    
  2. 自旋优化

    # 开启自适应自旋(默认开启)
    -XX:+UseSpinning
    -XX:PreBlockSpin=10  # 传统自旋次数(JDK6后无效)
    
  3. 监控工具

    # 查看锁竞争情况
    jstack <pid> | grep -A 10 "java.lang.Thread.State"
    
    # JFR记录锁事件
    jcmd <pid> JFR.start duration=60s filename=lock.jfr
    

八、与ReentrantLock对比

特性synchronizedReentrantLock
实现机制JVM内置,自动升级Java API实现
灵活性相对固定可中断、定时获取、公平锁等
性能JDK6后优化接近ReentrantLock高竞争下表现更好
锁降级不支持支持(通过ReadWriteLock)

锁升级机制是JVM对synchronized性能优化的核心设计,理解这一过程有助于:

  • 正确评估同步代码性能
  • 合理选择同步方案
  • 针对性优化高并发场景
http://www.dtcms.com/a/113311.html

相关文章:

  • ROS2 多机时间同步(Chrony配置简明指南)
  • docker本地部署anythingllm
  • C++重载运算符的本质
  • 将 DataFrame 中某一列的列表拆分成多个独立的列的方式
  • Linux上位机开发实践(做一专多能的方案提供者)
  • 从情感分析到朴素贝叶斯法:基于朴素贝叶斯的情感分析如何让DeepSeek赋能你的工作?
  • 【Kubernetes】RBAC(基于角色的访问控制)如何设置?如何管理 Kubernetes 的权限?
  • MCP 极简入门 - 三分钟 Cline + Smithery 运行 time 服务
  • HTTP/2:新一代网络协议的变革与优势
  • 借 DCMM 东风,提升数据管理价值生产力
  • BugKu Simple_SSTI_2
  • 【UE5 C++课程系列笔记】31——创建Json并保存为文件
  • 横扫SQL面试——TopN问题
  • 团体设计程序天梯赛L2-025 # 分而治之
  • Maven使用
  • 3535 数组分割
  • Python 数据库选型指南(架构师视角)
  • BUUCTF-web刷题篇(12)
  • 智能体和RPA都需要程序思维,如何使用影刀的变量?
  • HarmonyOS-ArkUI Ability进阶系列-UIAbility与各类Context
  • Python解决“数字插入”问题
  • 英语听力4.5
  • 6547网:蓝桥STEMA考试 Scratch 试卷(2025年3月)
  • Python作业3 字符田字格绘制
  • C语言之标准库中的常用api
  • 必刷算法100题之计算右侧小于当前元素的个数
  • 【算法竞赛】状态压缩型背包问题经典应用(蓝桥杯2019A4分糖果)
  • Linux数据库:【数据库基础】【库的操作】【表的操作】
  • [SAP SD] 常用事务码
  • Linux的/proc/sys/net/ipv6/conf/(all,default,interfaceName具体网络接口名称)/ 笔记250405