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

synchronized锁升级过程详解

synchronized 锁的升级是 Java 为了在没有竞争或竞争程度不同的情况下,平衡性能开销线程安全而设计的一套优化机制。它的核心思想是:当没有竞争时,使用代价最小的锁;当竞争加剧时,再逐步升级为更重量级的锁。

锁的状态从低到高依次为:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁。锁只能升级,不能降级(目的是为了提高获得锁和释放锁的效率)。


前置知识:Java 对象头 (Object Header)

要理解锁升级,必须先了解 Java 对象在内存中的布局,尤其是对象头。对象头主要包含两部分:

  1. Mark Word (标记字段):存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID等。这是锁升级过程发生的主要场所。

  2. Klass Pointer (类型指针):指向对象元数据的指针,JVM通过它来确定这个对象是哪个类的实例。

对象内存布局:

text

|--------------------------------------------------------------|
|                     Object Header (64 bits)                  |
|------------------------------------|-------------------------|
|        Mark Word (32 bits)         |    Klass Word (32 bits) |
|------------------------------------|-------------------------|

32位JVM中Mark Word的结构:

text

|-------------------------------------------------------|------|
|                  Mark Word (32 bits)                  | State|
|-------------------------------------------------------|------|
| identity_hashcode:25 | age:4 | biased_lock:0 | lock:01| 无锁 |
|-------------------------------------------------------|------|
|  thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:01| 偏向锁|
|-------------------------------------------------------|------|
|               ptr_to_lock_record:30          | lock:00| 轻量锁|
|-------------------------------------------------------|------|
|               ptr_to_heavyweight_monitor:30  | lock:10| 重量锁|
|-------------------------------------------------------|------|
|                                               | lock:11| GC   |
|-------------------------------------------------------|------|

注意:在偏向锁状态下,如果计算了对象的 hashCode(),则无法进入偏向锁状态,因为 Mark Word 没有空间同时存储线程ID和哈希码。

锁升级的详细过程

我们可以用一个流程图来概括整个过程,然后逐一详解:

现在,我们按照流程图的路径,详细讲解每一步。

第一阶段:无锁 -> 偏向锁 (Biased Locking)

  • 目的:在没有实际竞争的情况下,消除整个同步操作(如 monitorenter 和 monitorexit)的开销,进一步提高程序性能。它的假设是“在锁被持有期间,不会有其他线程来竞争”。

  • 触发条件:当 JVM 启用了偏向锁(-XX:+UseBiasedLocking,JDK 15 后默认关闭),并且一个对象被线程第一次访问时。JDK 15 默认关闭偏向锁是基于大量实际数据和性能分析做出的理性决策。

  • 详细过程

    1. 检查状态:线程 A 第一次进入同步块,检查对象头的 Mark Word。

    2. CAS 设置:发现锁标志位是 ‘01’,且偏向锁标志是 ‘0’(无锁状态)。于是,线程 A 使用 CAS 操作,尝试将自己的线程 ID 写入到对象的 Mark Word 中。

      • 如果 CAS 成功,线程 A 就获得了偏向锁。对象头的偏向锁标志位变为 ‘1’,锁标志位仍为 ‘01’。之后,只要没有其他线程来竞争,线程 A 每次进入这个同步块时,只需要检查 Mark Word 中的线程ID是否是自己,如果是,则直接进入,无需任何同步操作。

      • 如果 CAS 失败,说明在之前已经存在竞争,另一个线程 B 已经持有了这个偏向锁。此时,偏向锁需要撤销(Revoke Bias)

第二阶段:偏向锁 -> 轻量级锁 (Lightweight Locking)

  • 目的:当存在轻微的竞争(即线程交替执行同步块)时,避免直接使用重量级锁带来的操作系统内核态切换开销。

  • 触发条件:当有第二个线程 B 来尝试获取已经被线程 A 偏向的锁时。

  • 详细过程(偏向锁撤销与升级)

    1. 暂停线程 (STW):JVM 首先会安全点(Safepoint) 暂停拥有偏向锁的线程 A。

    2. 检查状态:检查线程 A 是否仍然活着或仍需要这个锁。

      • 如果线程 A 已经不活动或者已经退出同步代码块,则对象头可以重置为无锁状态,然后重新偏向给线程 B。

      • 如果线程 A 仍然需要这个锁(最常见的情况),则执行偏向锁撤销。

    3. 撤销与升级:JVM 会将对象头的 Mark Word 更新为指向线程 A 栈中锁记录(Lock Record)的指针。这个锁记录是 JVM 在各自线程的栈帧中创建的,用于存储锁对象的 Mark Word 拷贝(Displaced Mark Word)。

    4. 状态变更:此时,对象头的锁标志位变为 ‘00’,表示对象升级为轻量级锁状态

    5. 唤醒线程:恢复被暂停的线程 A。现在,线程 A 持有轻量级锁,线程 B 会通过自旋(循环CAS) 的方式尝试获取这个轻量级锁。

第三阶段:轻量级锁的运行与升级

  • 运行机制:在轻量级锁状态下,线程通过 CAS 操作(cmpxchg 指令)来尝试获取锁。它不会立即阻塞自己,而是在一个循环中自旋(忙等待),不断尝试获取锁。

  • 优点:如果锁被持有的时间很短,自旋的线程很快就能获得锁,避免了用户态到内核态的切换,开销很小。

  • 触发升级条件:如果线程 B 自旋了一定次数后(JDK 6 引入了自适应自旋,次数由前一次在同一个锁上的自旋时间及锁的拥有者状态来决定),还没有获取到锁。或者此时有第三个线程 C 也来参与竞争。

  • 详细过程

    1. 升级:轻量级锁会膨胀(Inflate) 为重量级锁。

    2. 状态变更:JVM 会创建一个监视器(Monitor) 对象(在 HotSpot 中称为 ObjectMonitor),并将对象头的 Mark Word 更新为指向这个 Monitor 的指针,锁标志位变为 ‘10’。

    3. 线程阻塞:此时,所有试图获取锁的线程(如线程 B 和 C)都会进入阻塞状态,并被放入 Monitor 的 EntryList 队列中等待。这些线程的状态会从 RUNNABLE 变为 BLOCKED

第四阶段:重量级锁 (Heavyweight Locking)

  • 运行机制:这是传统的 synchronized 锁的实现,依赖于操作系统底层的互斥量(Mutex Lock) 来实现。

  • 过程

    • 持有锁的线程 A 在执行完同步代码后,会调用 monitorexit 指令来释放锁。

    • 释放锁时,它会唤醒 EntryList 中阻塞的线程,这些线程需要重新竞争锁。

  • 开销:线程的阻塞和唤醒需要操作系统的介入,导致从用户态切换到内核态,开销非常大。


总结与要点

锁状态优点缺点适用场景
偏向锁加锁和解锁不需要CAS操作,性能开销极低如果存在锁竞争,会带来额外的撤销开销只有一个线程访问同步块
轻量级锁竞争的线程不会阻塞,提高了程序的响应速度如果始终得不到锁,自旋会消耗CPU追求响应时间,同步块执行速度非常快
重量级锁竞争的线程不使用CPU自旋,不会消耗CPU线程阻塞,响应时间缓慢,用户态内核态切换慢追求吞吐量,同步块执行时间较长,竞争激烈

核心思想:synchronized 不再是过去那个“笨重”的代名词。它通过这套精细的锁升级机制,实现了“按需分配”,在绝大多数没有竞争或低竞争的场景下,提供了接近无锁的性能;只有在高竞争的场景下,才会退化为开销较大的重量级锁。这正是 JVM 运行时优化的精髓所在。

http://www.dtcms.com/a/482763.html

相关文章:

  • mit6s081 lab8 locks
  • 建站培训企业管理考研
  • MySQL中的数据类型占用空间和范围
  • Docker部署jenkins集成全自动打包部署
  • 台州自助建站公司做好的网站怎么发布
  • 重磅更新:Claude Code 现在支持插件啦
  • 23种设计模式——访问者模式 (Visitor Pattern)
  • 卧龙区微网站开发网络管理系统怎么打开
  • java用注解优雅校验入参
  • 平台网站建设收费制作网页素材图片
  • 基于OpenCV的实时疲劳检测系统实现
  • HTML5基础——4、表格
  • Docker 通信核心:docker.sock 完全指南
  • Flutter---Button
  • 网站建设需要敲代码吗信息流推广实施策划书
  • 广元网站建设电子商务网站建设主要内容
  • 4.4-中间件之gRPC
  • 网络与通信安全课程复习汇总1——课程导入
  • Qt模型控件:QTreeViewQTreeWidget
  • 河南省城乡建设厅官网廊坊快速优化排名
  • 网站建设应该考虑哪些问题企信宝
  • 青海做网站最好的公司互联网推广引流
  • 一天一个设计模式——装饰器模式
  • 婚恋交友 APP 核心功能分析:从匹配逻辑到用户体验的全链路设计
  • 用一个 prompt 搭建带 React 界面的 Java 桌面应用
  • 宁波建网站价格wordpress注明网站
  • wordpress添加网站地图黑龙江新闻头条最新消息
  • 机械臂装配自动化推动紧固件设计革新
  • JSAR 入门教程:从零开始开发空间天气小摆件
  • 【Pytorch】什么是梯度