★ synchronized 底层原理是什么?锁升级过程是怎样的?
结论先行:synchronized 基于**监视器锁(Monitor)**实现:字节码层面通过 monitorenter/monitorexit 进入与退出,锁状态记录在对象头的 Mark Word 中。JDK 6 引入偏向锁后,锁沿“无锁 → 偏向锁 → 轻量级锁 → 重量级锁”单向升级,只有竞争足够激烈才膨胀为依赖 OS 互斥量的重量级锁。
锁升级路线
| 锁状态 | 触发条件 | 竞争代价 | 特点 |
|---|---|---|---|
| 无锁 / 偏向锁 | 首次进入,记录线程 ID 到 Mark Word | 极低 | 同一线程重复进入无 CAS |
| 轻量级锁 | 偏向失效、出现轻度竞争 | 低 | 自旋 + CAS 抢锁,不阻塞 |
| 重量级锁 | 自旋失败、竞争加剧 | 高 | 线程阻塞挂起,交给 OS 调度 |
配套机制
- Monitor 结构:每个 Java 对象关联一个 monitor;重量级锁时竞争线程进入 EntryList 排队,持锁者调用 wait 后进入 WaitSet。
- 可重入:同一线程重复进入 synchronized 会累加计数器,逐次退出递减,归零才真正释放。
- 不可中断:阻塞等待锁期间不能响应 interrupt,这是它和 Lock 的显著差异之一。
public class SyncDemo {
private int count;
public synchronized void incr() { // 修饰方法:锁对象是 this
count++;
}
public void incrBlock() {
synchronized (this) { // 同步块:可缩小加锁范围
count++;
}
}
public static synchronized void stat() { // 静态方法:锁 Class 对象
// 所有实例共享同一把类锁
}
}
常见追问 / 记忆点
- 记忆点:锁升级“偏向 → 轻量 → 重量”单向不可逆,竞争越激烈代价越高。
- 追问:JDK 15 起默认禁用偏向锁,因为现代应用竞争普遍,偏向收益已经变小。
- 追问:wait/notify 必须在 synchronized 块内调用,否则抛 IllegalMonitorStateException。