synchronized加锁解锁流程

一、加锁流程

synchronized 的加锁过程涉及 锁升级机制(偏向锁 → 轻量级锁 → 重量级锁),JVM 根据竞争情况动态调整锁状态以优化性能。以下是详细步骤:


1. 无锁状态 → 偏向锁
  • 场景:首次线程访问同步块,无竞争。

  • 流程:

    1. 检查对象头 :确认当前为无锁状态(锁标志位 01)。
    2. CAS 设置偏向线程 :将对象头中的 Mark Word 替换为当前线程 ID 和偏向时间戳(通过 CAS 操作)。
    3. 进入同步块:后续同一线程重入时,仅需检查线程 ID 是否匹配,无需额外操作。
  • 优点:无竞争时完全无同步开销。


2. 偏向锁 → 轻量级锁
  • 触发条件:其他线程尝试获取锁,触发偏向锁撤销。

  • 流程:

    1. 暂停持有偏向锁的线程:JVM 通过安全点(Safe Point)暂停线程。

    2. 撤销偏向锁:将对象头恢复为无锁状态,并记录锁撤销信息。

    3. 升级为轻量级锁:

      • 线程在栈帧中创建锁记录(Lock Record),存储对象头的 Mark Word 副本。
      • 通过 CAS 将对象头的 Mark Word 替换为指向锁记录的指针。
      • 若成功,线程获得轻量级锁;若失败,进入自旋等待。
  • 优点:竞争较小时通过 CAS 避免线程阻塞。


3. 轻量级锁 → 重量级锁
  • 触发条件:自旋超过阈值(默认 10 次)或竞争加剧。

  • 流程:

    1. 膨胀为重量级锁 :JVM 创建 Monitor 对象(C++ 的 ObjectMonitor),并将对象头指向 Monitor。
    2. 线程进入阻塞队列 :竞争失败的线程进入 Monitor 的 _EntryList 队列,等待操作系统调度。
    3. 切换至内核态:依赖操作系统的互斥量(Mutex Lock)实现线程阻塞。
  • 缺点:上下文切换开销大,性能下降。


二、解锁流程

解锁过程需处理不同锁状态,并唤醒等待线程:


1. 偏向锁解锁
  • 流程:

    1. 检查线程 ID:确认当前线程为锁持有者。
    2. 直接释放:无需修改对象头,保留偏向状态。
  • 特点:无竞争时解锁无开销。


2. 轻量级锁解锁
  • 流程:

    1. CAS 恢复对象头 :将锁记录中的 Mark Word 副本通过 CAS 写回对象头。
    2. 成功:对象恢复为无锁状态。
    3. 失败 :说明锁已膨胀为重量级锁,唤醒 Monitor 中的等待线程。
  • 特点:依赖 CAS 操作,无线程唤醒开销。


3. 重量级锁解锁
  • 流程:

    1. 释放 Monitor :将 Monitor 的 _owner 字段置为 null。
    2. 唤醒等待线程 :从 _EntryList 或 _WaitSet 队列中唤醒一个线程。
    3. 上下文切换:操作系统调度唤醒的线程重新竞争锁。
  • 特点:涉及内核态切换,开销较大。


三、内存语义与屏障

  • 加锁时:

    • Load 屏障:强制从主内存加载共享变量最新值。
    • Acquire 屏障:禁止同步块内读操作与块外指令重排序。
  • 解锁时:

    • Release 屏障:强制将工作内存的修改刷新到主内存。
    • Store 屏障:禁止同步块内写操作与块外指令重排序。

四、流程图解

scss 复制代码
加锁流程:
[无锁] → (首次线程访问) → [偏向锁] → (竞争发生) → [轻量级锁] → (自旋失败) → [重量级锁]
          ↑_________________________↓               ↑_______________↓
​
解锁流程:
[偏向锁] → 无操作
[轻量级锁] → CAS 恢复对象头
[重量级锁] → 释放 Monitor,唤醒线程

五、性能优化策略

  1. 减少锁竞争:

    • 缩小同步范围(如细化锁粒度)。
    • 使用无锁数据结构(如 ConcurrentHashMap)。
  2. 避免锁升级:

    • 控制同步块执行时间(减少自旋失败概率)。
  3. 锁消除(Lock Elision) :

    • JIT 编译器优化,去除不可能竞争的锁(如局部变量锁)。
  4. 锁粗化(Lock Coarsening) :

    • 合并相邻的同步块,减少锁获取/释放次数。

六、总结

阶段 触发条件 性能开销 适用场景
偏向锁 单线程重复访问 极低 无竞争环境
轻量级锁 低并发竞争 低(CAS) 短时间同步块
重量级锁 高并发竞争 高(阻塞) 长时间同步块或高并发

关键点:

  • 偏向锁 和 轻量级锁 是 JVM 的优化手段,旨在减少无竞争或低竞争时的开销。
  • 重量级锁 是最终兜底方案,依赖操作系统实现线程阻塞与唤醒。
  • 理解锁升级流程有助于编写高效并发代码,避免不必要的性能损耗。
相关推荐
云和数据.ChenGuang3 小时前
Python 重写(Override)深度解析:从 MRO 到 super() 的完整机制
java·开发语言·人工智能·python·langchain·1024程序员节
摇滚侠3 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 12
java·数据库·笔记
silvamux_5 小时前
10款模型写种草笔记:可发布性、返工来源与成本数据缺口复盘
java·数据库·笔记
步行cgn6 小时前
StringHttpMessageConverter 详解:Spring MVC 中字符串读写的默认转换器
java·spring·mvc
摇滚侠6 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 11
java·笔记·python
小蒜学长6 小时前
基于Spring Boot+Vue3的在线医疗咨询系统的设计与实现(代码+数据库+LW)
java·spring boot·后端·在线问诊·在线医疗咨询系统
Deng桑6 小时前
SpringAI写第一个Agent-把工单循环跑起来
java·agent·工具调用·spring ai
步行cgn8 小时前
@ResponseBody 详解:Spring MVC 中把返回值直接写入响应体
java·spring·mvc
jason成都9 小时前
RTKLIB 的 Java 移植版来了:rtklib-java v3.3.0 功能全景
java·gnss·rtklib·rtklib_java
LazyHunter99 小时前
JDK 28:Java 下一步方向解析与新特性展望
java·jdk