面试知识库
极高 困难

synchronized原理与锁升级#

一句话答案#

synchronized 底层靠 Monitor 实现,JDK6 引入锁升级:无锁→偏向锁(单线程)→轻量级锁(CAS)→重量级锁(阻塞)。

核心要点

synchronized 的底层实现(Monitor 对象监视器):

每个 Java 对象都有一个关联的 Monitor(监视器),存储在对象头(Mark Word)中。

代码块锁:
synchronized(obj) { ... }
↓ 编译为字节码:
monitorenter   ← 获取 obj 关联的 Monitor,计数+1
  ...
monitorexit    ← 释放 Monitor,计数-1,计数=0时唤醒等待线程

方法锁:
synchronized void method() { ... }
↓ 方法访问标志中设置 ACC_SYNCHRONIZED,JVM 执行时自动 monitorenter/monitorexit
plaintext

锁升级(JDK 6 引入,解决 synchronized 重量级锁性能问题):

锁状态存储在对象头的 Mark Word 中,随竞争程度单向升级(不可降级,JDK 15 后偏向锁被移除):

为什么自旋后才升级重量级锁: 如果持锁时间很短(如纳秒级),与其让线程阻塞(涉及上下文切换,微秒级开销),不如让它 CAS 自旋几次等待,避免不必要的内核调用。

面试回答(2分钟版)

synchronized 底层通过 Monitor 对象监视器实现,代码块编译为 monitorenter 和 monitorexit 字节码,方法则在访问标志中设置 ACC_SYNCHRONIZED。JDK6 之前 synchronized 直接使用重量级锁,涉及用户态和内核态切换开销很大。JDK6 引入了锁升级机制,锁状态记录在对象头的 Mark Word 中,分为四个级别:无锁状态下第一个线程进入时升级为偏向锁,Mark Word 记录线程 ID,同一线程再次获取时直接比对 ID 免去 CAS,极快;当第二个线程来竞争时升级为轻量级锁,线程在栈帧中创建 Lock Record 并 CAS 将其地址写入 Mark Word;CAS 自旋超过阈值(默认约 10 次,JVM 自适应调整)仍未获锁则升级为重量级锁,Mark Word 指向 ObjectMonitor,获锁失败的线程进入 EntryList 阻塞等待。整个过程是单向升级不可降级。自旋的意义在于持锁时间很短时避免线程阻塞带来的上下文切换开销,比直接阻塞更高效。

追问与易错

追问方向:

  • “锁升级后能降级吗?”→ 一般不可逆,特殊情况 GC 时可能降级
  • “JDK15 为什么默认关闭偏向锁?”→ 偏向撤销代价大,现代应用竞争多
  • “synchronized 加在方法和代码块上的区别?”→ 方法用 ACC_SYNCHRONIZED 标志,代码块用 monitorenter/exit

易错点:

  • ❌ “synchronized 性能很差”——JDK6 优化后轻量级锁开销很小
  • ❌ 不理解”锁的是对象不是代码”——不同对象实例互不影响