锁优化是 JVM 实现中对 synchronized 锁的优化处理
偏向锁
JDK 6 开始默认开启偏向锁,也就是默认的 synchronized 锁首先是个可偏向锁,锁会偏向于第一个获得它的线程。如果接下来一段时间没有其他线程来获取锁,那么这个线程进入或退出同一个同步代码块是就不需要抢占、释放锁,当仍然会记录进入次数。
偏向是通过将同步对象的对象头 Mark Word 设为偏向模式,并利用 CAS 将第一个线程 ID 记录在 Mark Word 中,该线程通过检查 Mark Word 得知该偏向锁偏向自己。一旦有另一个线程在检查 Mark Word 时发现同步对象不偏向自己,偏向锁状态宣告结束,该同步对象变为不可偏向状态。
对象计算一致性哈希(hashCode),或者被第二个线程检查后,就再也无法进入偏向状态。
轻量级锁
不可偏向的同步对象,被一个线程尝试获取时,该线程在栈帧中开辟空间(Lock Record)保存锁对象的 Mark Word,并利用 CAS 修改 Mark Word 指向 Lock Record 地址。
修改成功就获得了轻量级锁,释放锁时恢复原来的 Mark Word;修改失败,说明同时存在竞争,就膨胀为重量级锁。
自旋锁
线程的阻塞和唤醒需要 CPU 在用户态和内核态之间切换。JDK 6 开始默认开启自适应自旋锁,在获取重量级锁时,让线程忙循环♻️,不会立即挂起,超过限度的次数仍没有获得锁,再以传统方式挂起线程。
总结
- 偏向锁,用于只有 1 个线程会访问同步代码的场景;
- 轻量级锁,用于可能有多线程访问,但不会同时访问,即没有竞争的场景;
- 自旋锁时对重量级锁的优化,用忙循环代替阻塞,使用于锁被占用时间很短的场景
参考
本文基于周志明博士著《深入理解Java虚拟机(第3版)》,加上个人的理解,整理而成。