← 每日面试题 学习看板

synchronized是怎么实现的?

面试题/00java/02Java 并发编程/06线程同步与锁机制/synchronized是怎么实现的?.md

面试记录 2026-09-26

题目:synchronized 是怎么实现的?

难度:medium

标签:java/lock, general/knowledge

评分:⭐⭐⭐☆☆

我的回答

Synchronized 的本质上就是在对象头上做限定,定义当前这个对象是否被加了锁。当线程进行访问的时候,确认这个线程持有的线程是什么。啊,如果是当前线程就可以正常访问,如果不是当前线程,就放入队列等待。核心核心原理其实就这个,然后在 1.8 之后做了一系列的优化,包括锁升级,然后锁的力度的调整,这些东西。它核心的就是我刚才说的,在对象头中记录锁的信息、线程的信息,确保是当前线程去访问这个对象,而不是其他线程进入的时候,也能访问到这个对象。然后当其他线程访问的时候做一个阻塞。核心原理就是这个。哦,JDK1.8 的时候做了一个优化升级,核心的作用就是优化线程内的性能,不是一上来就用极重的锁持有,最开始使用的就是轻向锁。你一个线程来了,保持有一个记录下来是你这个线程,但是并不加锁。而后如果多个线程进来了,就开始升级变成轻量级锁,做简单的重试等待,也不是直接加锁,类似于 CAS 的操作。然后再往后,更多的线程并发产生了,才直接上锁,做重量级的锁持有限定。确保一个线程只被等待,其他线程只确保一个线程在使用,其他线程要等待做阻塞。

点评

优点:抓到核心点——对象头(Mark Word)记录锁信息;提到锁升级、CAS、性能优化

不足:锁升级顺序描述有误(跳过了偏向锁);未提 Mark Word、Monitor、monitorenter/monitorexit 术语;未说三种使用场景;未提 JDK 15/18 偏向锁废弃新知识点

补充:正确路径:无锁→偏向锁→轻量级锁→重量级锁;对象头结构 Mark Word+Klass Pointer;JDK 15 默认关闭偏向锁,JDK 18 彻底移除

完整讲解

第一部分:知识重点剖析

核心考点定位:能否说清对象头 + Mark Word + 锁升级四条路径 + 字节码层实现。

知识点拆解:

  1. 对象头结构(HotSpot 64 位):
  1. 锁的三种使用场景:
  1. 锁升级四条路径(JDK 6 引入,JDK 15 后偏向锁被默认关闭,JDK 18 彻底移除):
  1. 字节码层:
  1. Monitor(管程):重量级锁的真正实现,每个对象都有一个隐含的 Monitor 对象,包含 owner、waitSet、entryList 等字段
按「底层原理 → 设计权衡 → 工程实践」递进排列。

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

考察方向:

延伸递进面试题:

  1. synchronized 的锁能降级吗?
  1. 偏向锁为什么被废弃了?
  1. synchronized 和 ReentrantLock 怎么选?

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

面试官您好,synchronized 的实现我分三个层次来说:字节码层、对象头层、锁升级层。

字节码层,synchronized 有三种使用方式:修饰实例方法锁 this、修饰静态方法锁 Class 对象、修饰代码块锁指定对象。在字节码层面,synchronized 方法有 ACC_SYNCHRONIZED 标志位,synchronized 代码块使用 monitorenter 和 monitorexit 两条指令(异常路径下还有 athrow 保证锁释放)。

对象头层是核心。HotSpot 64 位对象的对象头主要由两部分组成:Mark Word(8 字节,存哈希码、GC 分代年龄、锁标志位、锁指针/线程 ID)和 Klass Pointer(4-8 字节,指向方法区的元数据)。Mark Word 里的锁标志位(最后 2 位)直接决定了对象当前是什么锁状态。

锁升级层是 synchronized 性能优化的关键,从低到高四条路径:

第一条是 无锁 → 偏向锁。当只有一个线程访问时,Mark Word 会记录这个线程的 ID,之后同线程访问直接放行,不加锁也不 CAS。这是最轻量化的状态,适合"一次获取、多次访问"的低竞争场景。不过要注意 JDK 15 之后偏向锁被默认关闭,JDK 18 已经彻底移除了,因为偏向锁撤销代价高、需要 STW,在现代 GC 下不划算。

第二条是 偏向锁 → 轻量级锁。当第二个线程到达时撤销偏向锁,升级为轻量级锁;线程用 CAS 自旋尝试获取,不进入 OS 阻塞。适合"锁持有时间极短"的中竞争场景。

第三条是 轻量级锁 → 重量级锁。当 CAS 自旋失败达到阈值(默认 10 次,可用 -XX:PreBlockSpin 调),就膨胀为重量级锁,线程挂起进入 OS 内核调度,通过 Monitor 的 entryList 排队。重量级锁是不可降级的——JVM 保证锁只有升级没有降级路径。

重量级锁的真正实现是 Monitor(管程),每个对象都隐含一个 Monitor 对象,包含 owner、entryList、waitSet 等字段,wait() 方法就是把线程挂到 waitSet 上。

和 ReentrantLock 的对比:低竞争场景 synchronized 更合适(JVM 内置、零开销、代码简洁),高竞争或需要中断/超时/公平锁/多条件变量时用 ReentrantLock。

这就是我对 synchronized 实现的理解。