synchronized 是 JVM 内置的可重入、非公平互斥锁 ,底层基于对象头 MarkWord + Monitor 监视器 实现,JDK 1.6 后通过锁升级大幅优化性能,保证并发下的原子性、可见性、有序性。
二、底层实现原理(核心必答)
-
锁的本质:锁的是对象,不是代码
- 修饰实例方法:锁当前对象
this - 修饰静态方法:锁当前类的
Class对象 - 修饰代码块:锁括号里指定的对象
- 修饰实例方法:锁当前对象
-
对象头与 Monitor
- Java 对象在堆中分为:对象头、实例数据、对齐填充。
- MarkWord 存储:哈希码、分代年龄、锁状态、线程 ID。
- 每个对象关联一个 Monitor(ObjectMonitor) ,由 C++ 实现,包含:
_owner:持有锁的线程_count:重入计数_EntryList:阻塞等待队列_WaitSet:调用 wait () 的等待队列
-
字节码指令
- 同步代码块:
monitorenter加锁、monitorexit解锁。 - JVM 会自动生成两个 monitorexit,保证正常 / 异常都能释放锁。
- 同步方法:通过方法标识
ACC_SYNCHRONIZED实现。
- 同步代码块:
三、锁升级流程(JDK 1.6+ 最重要考点)
不可逆流程:无锁 → 偏向锁 → 轻量级锁 → 重量级锁
-
偏向锁
- 适用:单线程反复获取锁,无竞争
- MarkWord 记录线程 ID,后续直接进入,无同步开销
- 一旦出现其他线程竞争,立即升级为轻量级锁
-
轻量级锁
- 适用:线程交替执行,竞争时间短
- 线程在栈帧中创建锁记录,用 CAS 尝试抢占锁
- 竞争失败则自旋,自旋一定次数仍失败 → 升级重量级锁
-
重量级锁
- 依赖操作系统
Mutex Lock互斥量 - 线程进入内核态阻塞,放入 EntryList
- 开销大,但不占用 CPU
- 依赖操作系统
四、三大特性
-
可重入 同一线程可多次获取同一把锁,底层靠
_count计数实现,避免死锁。 -
非公平锁线程竞争不按等待顺序,新线程可直接抢占,吞吐量高,但可能产生饥饿。
-
保证可见性 加锁时从主存刷新,解锁时写回主存,底层通过内存屏障实现 Happens-Before。
五、高频面试题答案
1. synchronized 保证了什么?
原子性、可见性、有序性。
2. 为什么是可重入锁?
通过 Monitor 中的 _count 计数器实现,同一线程加锁计数 + 1,解锁 - 1,为 0 才真正释放。
3. 锁升级为什么不可逆?
为了简化 JVM 实现,避免状态回退带来的复杂度和并发安全问题。
4. synchronized 和 ReentrantLock 区别?
- synchronized:JVM 实现,自动加解锁,非公平,功能少
- ReentrantLock:JDK 实现,手动加解锁,可公平 / 非公平,支持条件等待、超时、中断
六、精简版
synchronized 是 JVM 内置的互斥锁,底层基于对象头 MarkWord 和Monitor 监视器 ,通过 monitorenter/monitorexit 指令实现。JDK 1.6 后引入锁升级 :无锁→偏向锁→轻量级锁→重量级锁,用来减少高并发开销。它是可重入、非公平锁,能保证原子性、可见性、有序性。