← 每日面试题 学习看板

synchronized的锁升级过程是怎样的?

面试题/00java/02Java 并发编程/06线程同步与锁机制/synchronized的锁升级过程是怎样的?.md

面试记录 2026-09-29

题目:synchronized 的锁升级过程是怎样的?

难度:medium

标签:java/lock, difficulty/medium

评分:⭐⭐⭐☆☆(3/5)

我的回答

锁升级是 JDK 1.8 之后,在原有的它的线程管理体系上做的一个优化。核心目的是为了控制线程的管理消耗的资源。它的核心是在于在轻量级的时候使用无锁或者简单的锁去进行资源管控,线程的资源管控。只有当竞争比较激烈的时候,才使用重量级的锁,真正的锁去控制线程的进入、访问。最初是没有访问的时候是无锁的。当有单级访单个线程进入访问的时候,在它结束之前没有其他线程,这个锁是偏向锁,就是偏向这个当前这个线程。偏向锁的时候,它会在线程头记录当前这个线程的信息。当有这个线程进入的,这个线程进入的时候,它就给这个线程,使用这个线程去执行。当有其他线程进行竞争的时候,竞争量并不大,开始进入轻量级锁,升级到轻量级锁。轻量级锁是类似于 CAS,它并不是把线程阻塞,而是让线程在那自循环,不会让线程释放掉。在自循环的过程中,线程还是活跃的,并不会释放。当前一个线程执行完之后,环的线程才进入新的,才开始新进行持有锁,继续往下执行。但是当线程等待超过一定次数,循环一定次数之后,会再次升级,或者是同时进入的线程数量比较多的时候,就直接升级了,升级为重量级锁。这时候单个线程在执行,其他线程就被阻塞,释放资源,等待前一个线程完成,然后唤醒其他线程进行锁的争抢。另外,升级不能降级,锁只能升级不能降级。

点评

优点:

不足:

补充(必背):

四状态对照表:

状态触发条件底层实现Mark Word 存储
无锁未被任何线程持有—对象标记 + 分代年龄 + GC 标志
偏向锁首次访问,只有一个线程CAS 写入线程 ID,之后无同步开销偏向锁标志 + 线程 ID
轻量级锁有第二个线程竞争CAS 自旋,Mark Word 拷贝到 Lock Record轻量级锁标志 + 指向 Lock Record 的指针
重量级锁自旋超过阈值或多线程争抢系统调用进入内核态,线程挂起重量级锁标志 + 指向 monitor 的指针

JDK 版本演进(重要):

JIT 的额外优化:

面试杀手锏:

  1. 偏向锁撤销不是"锁降级",而是"撤销偏向 + 升级到轻量级"
  2. 单线程场景偏向锁延迟 40ms 才偏,因为要避免频繁撤销
  3. 现代 JVM(16+)实际只有"无锁 → 轻量级 → 重量级"三态

完整讲解

第一部分:知识重点剖析

核心考点定位:考察对 synchronized 底层优化机制的理解,尤其偏向锁的生命周期与 JDK 版本演进。

知识点拆解:

  1. 底层原理:锁升级本质是 JIT 编译器针对不同竞争程度选择不同实现策略——单线程走 CAS 无竞争、双线程走自旋、多冲突走内核阻塞,全部靠 Mark Word 里的 4 位锁标志位切换
  2. 设计权衡:偏向锁在单线程场景零开销,但撤销代价高(需要等所有持有偏向锁的代码块执行完或全局安全点);轻量级锁自旋在 CPU 空闲时好,CPU 忙时会浪费;重量级锁有线程挂起的系统调用开销,但真正多冲突时最省心
  3. 工程实践:现代 JDK(16+)默认关闭偏向锁,因此实际开发中"锁升级"更多是"轻量级 → 重量级"两态;业务代码应尽量避免长时间持有锁,让锁停留在轻量级

第二部分:考察方向与延伸递进

考察方向:

延伸递进面试题:

  1. 偏向锁为什么在 JDK 15 被默认关闭?
  1. 如何判断锁是否升级到重量级?
  1. synchronized 和 ReentrantLock 的选型依据是什么?

第三部分:面试者参考回答(300-600 字口语化)

面试官您好,synchronized 的锁升级从 JDK 1.5 就引入了,本质是 JIT 根据竞争程度选择不同实现策略,全部靠对象头 Mark Word 里的 4 位标志位切换。

一共四态。无锁是初始状态,Mark Word 存对象元数据。偏向锁在单线程访问时启用,把当前线程 ID 写进 Mark Word,后续这个线程再访问时 CAS 校验线程 ID 匹配就无需加解锁,开销接近零。JDK 15 之后偏向锁默认关闭,JDK 18 彻底移除,原因是撤销代价高、G1 的 safepoint 频繁。轻量级锁在有第二个线程竞争时启用,把 Mark Word 拷贝到线程栈上的 Lock Record,然后 CAS 自旋,其他线程不会挂起。重量级锁在自旋超过阈值或多线程争抢时启用,通过操作系统 mutex 挂起竞争线程,有系统调用开销。

升级方向是不可逆的,但每次解锁后 Mark Word 会回到无锁状态,下次加锁可能重新走偏向锁。JIT 还有两个额外优化:锁消除(局部变量 StringBuilder 的 synchronized 会被直接删掉)、锁粗化(连续对同一对象加解锁合并成大锁)。

工程上,JDK 8 是"四态全开"的黄金版本;JDK 16+ 实际就是"轻量级 → 重量级"两态。业务要避免长时间持有锁,让锁停留在轻量级阶段。选型上默认用 synchronized,需要超时、中断、公平或多 condition 时才用 ReentrantLock。


面试记录 2026-10-17(第 2 次)

题目:synchronized 的锁升级过程是怎样的?

难度:medium

标签:java/lock, general/knowledge

评分:⭐⭐⭐⭐☆(4/5)

练习次数:2

我的回答

synchronize,在jdk1.6之后增加了锁升级的逻辑,整个升级过程是根据锁的对象被访问的线程频率来的。当没有线程访问的时候是无锁的; 当有单个线程访问,没有并发线程进入的时候,是偏向锁,在线程头部记录当前线程,如果一致则直接访问不需要做什么控制判断; 当有多个线程访问,但是竞争度不高的时候使用的是轻量级锁,或者说自旋锁,持有锁的线程执行过程中,其他线程在自循环,等待持有者释放锁,释放后新线程直接持有锁,其他线程继续自旋,当自旋到一定次数还没有获取锁后就会进行锁升级,转为重量级锁,这时候直接是使用操作系统底层的锁机制来进行线程控制,持有锁的线程正常执行,其他线程进入阻塞状态,等待持有锁线程执行完毕唤醒所有阻塞中的线程争抢锁的持有状态

点评

优点:四态路径完整(无锁→偏向→轻量级→重量级)、触发条件准确(单线程→偏向、多线程低竞争→轻量级、自旋超阈值→重量级)、"对象头记录线程 ID"和"重量级用 OS 挂起"都讲到了;比首次(2026-09-29,3/5)更有条理。

不足:版本事实仍错("JDK 1.6 之后"——实际 1.5 引入、JDK 15 默认关偏向、JDK 18 移除);漏 Lock Record 结构、锁消除/锁粗化、"升级不可逆"的准确表述。

补充:现代 JDK(16+)实际只有"无锁 → 轻量级 → 重量级"三态。

ℹ️ 完整讲解见上方首次记录(2026-09-29)