前言
🍊作者简介: 不肯过江东丶,一个来自二线城市的程序员,致力于用“猥琐”办法解决繁琐问题,让复杂的问题变得通俗易懂。
🍊支持作者: 点赞👍、关注💖、留言💌~
“面试造火箭,入职拧螺丝?” 别慌!这里没有“茴香豆的茴有几种写法”,只有最实用、最高频、最能唬住面试官的 Java 面试题解析!
你是不是也经历过:
- 🤯 背了一堆八股文,面试官一问就懵圈?
- 😅 明明知道答案,却讲得支支吾吾?
- 🚀 想进大厂,却连 HashMap 和 ArrayList 的区别都说不利索?
别怕!这就是你的“Java面试急救包”,大聪明专治各种答不上来😉
正文
💡每日一题:Sychronized 锁升级过程是什么?
💡难度系数: ⭐⭐
synchronized 是 Java 提供的内置锁(Monitor Lock),用于保证多线程环境下的同步访问。在说 synchronized 锁的升级过程之前,我们先来简单了解一下 synchronized 的实现原理 👇
JVM 通过 monitorenter 和 monitorexit 字节码指令实现锁的获取和释放:
- monitorenter:尝试获取锁(进入 Monitor)
- monitorexit:释放锁(退出 Monitor)
锁信息存储在对象头中的 Mark Word:
- 32 位 JVM:Mark Word 占 32 bits
- 64 位 JVM:Mark Word 占 64 bits
对象头(Mark Word)存储了锁状态信息,不同锁状态下其结构也有所不同:
锁状态 | 存储内容 |
---|---|
无锁(Unlocked) | hashCode + 分代年龄 + 锁标志位 01 |
偏向锁(Biased) | 线程 ID + Epoch + 分代年龄 + 锁标志位 01 |
轻量级锁(Lightweight) | 指向栈中锁记录的指针 + 锁标志位 00 |
重量级锁(Heavyweight) | 指向 Monitor(互斥量)的指针 + 锁标志位 10 |
GC 标记 | 空(用于垃圾回收) + 锁标志位 11 |
Java 6 之后,synchronized 采用锁升级(Lock Escalation)机制,从 无锁 → 偏向锁 → 轻量级锁 → 重量级锁 逐步升级,以减少锁竞争带来的性能开销。
1、无锁(Unlocked)
- 初始状态,对象刚创建时没有线程竞争。
- Mark Word 存储 hashCode + 分代年龄 + 锁标志位 01。
2、偏向锁(Biased Locking)
- 适用场景:只有一个线程访问同步代码块。
- 实现方式:JVM 在 Mark Word 中记录 线程 ID,表示该线程持有偏向锁。后续该线程进入同步代码块时,无需 CAS 操作,直接检查 Mark Word 中的线程 ID 是否匹配。
- 优点:减少 CAS 操作,提高单线程访问性能。
- 撤销偏向锁:如果另一个线程尝试获取锁,JVM 会撤销偏向锁,升级为轻量级锁。
3、轻量级锁(Lightweight Locking)
- 适用场景:多个线程 交替执行(无竞争或竞争不激烈)。
- 实现方式:JVM 在当前线程的栈帧中创建 锁记录(Lock Record),并拷贝 Mark Word 到锁记录。使用 CAS 尝试将 Mark Word 替换为指向锁记录的指针。如果 CAS 成功,当前线程获取轻量级锁;如果 CAS 失败(竞争发生),则升级为 重量级锁。
- 优点:避免直接进入重量级锁,减少线程阻塞。
4、重量级锁(Heavyweight Locking)
- 适用场景:多个线程 竞争激烈(长时间等待锁)。
- 实现方式:JVM 分配一个 Monitor 对象(ObjectMonitor),Mark Word 指向该 Monitor。未获取锁的线程进入 阻塞队列(EntryList),等待唤醒。
- 缺点:涉及 用户态 → 内核态切换,性能较低。
小结
📢 面试不是终点,而是技术成长的起点!持续关注本专栏,更多硬核内容在路上! 🚀
💻PS:如果觉得有用,别忘了点赞+收藏,你的支持是我更新的最大动力!❤️