为什么有锁升级:性能优化的智慧
锁升级的核心目标是在保证线程安全的前提下,最大限度地减少锁带来的性能开销。
场景比喻
用一个简单易懂的'办公室会议室'比喻,给你讲清楚 Java 的锁升级。
首先明确一点:锁升级是 JVM 为了优化 synchronized 关键字性能而设计的内部机制。它的核心思想是:大多数情况下,锁不仅不存在竞争,而且总是由同一个线程多次获得。 因此,没必要一开始就用'大炮'(重量级锁),而是从'小装备'开始,根据需要逐步升级。
整个过程分为 4 个状态,我们想象一个'锁对象'(比如一个会议室)的生命周期:
1. 无锁状态
- 场景:会议室(锁对象)刚建好,还没人用。任何线程(员工)都可以直接进去使用。
- 对应:一个新创建的对象,还没被任何线程加锁。
2. 偏向锁(单线程多次访问)
- 核心思想:这个锁'偏爱'第一个来用它的人。 假设员工 A 第一个使用了会议室。
- JVM 操作:JVM 会在这个会议室门口贴一张'常驻牌',上面写着'A 专属'。同时,在对象头里记录 A 的线程 ID。
- 后续:
- 如果 A 下次又来用会议室,一看牌子是自己的,直接进去,没有任何额外检查,开销极低。
- 如果员工 B 也想用,会发现牌子是 A 的(发生了竞争)。JVM 会先暂停 A,检查 A 是否还在用会议室。
- 如果 A 已经用完了(同步代码块已退出),JVM 就把牌子撕掉,然后重新贴成 B 的(偏向锁撤销并重偏向)。
- 如果 A 还在用,说明有竞争,偏向锁就需要升级。
- 目的:消除同一线程重入锁的开销。适用于只有一个线程反复进入同步块的场景。
3. 轻量级锁 / 自旋锁(少量线程短时间竞争)
- 场景:当偏向锁因为竞争(B 也想用)而升级后,就进入这个状态。
- 比喻:现在会议室没有'常驻牌'了。A 在里面开会,B 来了。B 不会去'领导办公室'(操作系统内核)那里投诉,而是选择在门口等一会儿(自旋),边等边嘀咕:'A 应该快出来了吧?我再等等看……'
- JVM 操作:
- JVM 会在当前线程的栈帧中创建一个'锁记录空间',并拷贝会议室门上的标记。
- 然后尝试用CAS 操作(一种乐观的原子操作)把会议室门牌换成指向自己'锁记录'的指针。如果成功,就拿到锁。
- 竞争线程(B)通过自旋(循环尝试 CAS)来尝试获取锁。
- 优点:避免了直接惊动操作系统内核(重量级操作),在竞争不激烈、等待时间极短的情况下效率很高。
- 缺点:如果 B 在门口等得太久(自旋超过一定次数或自旋线程数增加),就会白占着 CPU 资源,属于'空转',得不偿失。
4. 重量级锁(真正激烈的多线程竞争)
- 场景:如果 B 在门口自旋等了很久(比如自旋了 10 次),A 还没出来,或者又有 C、D、E…一群员工都在门口自旋等着。
- JVM 操作:这时候,JVM 就会判定竞争太激烈了。它会将轻量级锁升级为重量级锁。



