题目:synchronized 的锁升级过程是怎样的?
难度:medium
标签:java/lock, difficulty/medium
评分:⭐⭐⭐☆☆(3/5)
锁升级是 JDK 1.8 之后,在原有的它的线程管理体系上做的一个优化。核心目的是为了控制线程的管理消耗的资源。它的核心是在于在轻量级的时候使用无锁或者简单的锁去进行资源管控,线程的资源管控。只有当竞争比较激烈的时候,才使用重量级的锁,真正的锁去控制线程的进入、访问。最初是没有访问的时候是无锁的。当有单级访单个线程进入访问的时候,在它结束之前没有其他线程,这个锁是偏向锁,就是偏向这个当前这个线程。偏向锁的时候,它会在线程头记录当前这个线程的信息。当有这个线程进入的,这个线程进入的时候,它就给这个线程,使用这个线程去执行。当有其他线程进行竞争的时候,竞争量并不大,开始进入轻量级锁,升级到轻量级锁。轻量级锁是类似于 CAS,它并不是把线程阻塞,而是让线程在那自循环,不会让线程释放掉。在自循环的过程中,线程还是活跃的,并不会释放。当前一个线程执行完之后,环的线程才进入新的,才开始新进行持有锁,继续往下执行。但是当线程等待超过一定次数,循环一定次数之后,会再次升级,或者是同时进入的线程数量比较多的时候,就直接升级了,升级为重量级锁。这时候单个线程在执行,其他线程就被阻塞,释放资源,等待前一个线程完成,然后唤醒其他线程进行锁的争抢。另外,升级不能降级,锁只能升级不能降级。
优点:
不足:
-XX:PredeclareSpinCount)、偏向锁被撤销的条件(G1 下可能提前撤销)补充(必背):
四状态对照表:
| 状态 | 触发条件 | 底层实现 | Mark Word 存储 |
|---|---|---|---|
| 无锁 | 未被任何线程持有 | — | 对象标记 + 分代年龄 + GC 标志 |
| 偏向锁 | 首次访问,只有一个线程 | CAS 写入线程 ID,之后无同步开销 | 偏向锁标志 + 线程 ID |
| 轻量级锁 | 有第二个线程竞争 | CAS 自旋,Mark Word 拷贝到 Lock Record | 轻量级锁标志 + 指向 Lock Record 的指针 |
| 重量级锁 | 自旋超过阈值或多线程争抢 | 系统调用进入内核态,线程挂起 | 重量级锁标志 + 指向 monitor 的指针 |
JDK 版本演进(重要):
-XX:+UseBiasedLocking)-XX:-UseBiasedLocking),因为延迟偏斜代价高JIT 的额外优化:
面试杀手锏:
核心考点定位:考察对 synchronized 底层优化机制的理解,尤其偏向锁的生命周期与 JDK 版本演进。
知识点拆解:
考察方向:
ReentrantLock 的公平/非公平、StampedLock 的读优化对比jcmd、-XX:+PrintLocks 观察锁状态;如何避免锁升级到重量级延伸递进面试题:
jcmd <pid> GC.heap_dump 后用 MAT 查看 Mark Word;或开启 -XX:+PrintLocks面试官您好,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。
题目: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)