java锁升级路径为无锁→偏向锁→轻量级锁→重量级锁,不可逆;依据对象头mark word中锁标志位动态判定,偏向锁首次进入同步块时cas设置线程id,轻量级锁通过栈帧lock record与cas实现自旋抢锁,超限即升级,重量级锁触发内核态阻塞。

锁升级到底走哪几步?路径不可逆是硬约束
Java锁升级只有一条单向路径:无锁 → 偏向锁 → 轻量级锁 → 重量级锁,且**绝不会降级**。这不是设计选择,而是JVM实现决定的——撤销偏向锁要STW(Stop-The-World),轻量级锁自旋失败后直接膨胀为重量级锁,没有“回头路”。面试答错“可降级”或漏掉“无锁”起点,基本就扣分了。
关键判断依据全在对象头的Mark Word里:32/64位中用2bit存锁标志位(如01表示偏向锁,00表示轻量级锁,10表示重量级锁),再配合1bit是否偏向锁标识。不是靠代码写法,而是JVM运行时根据竞争动态改写这个字段。
偏向锁怎么生效?别被“自动加锁”误导
偏向锁不是“一声明就上锁”,而是**首次进入同步块时触发**:JVM用一次CAS把当前线程ID写入对象头Mark Word,并设标志为01。之后同一线程再次进入,只需比对线程ID,连CAS都不用——这才是性能优势所在。
- 失效场景很明确:第二个线程尝试获取同一把锁,就会触发偏向锁撤销(revoke);若此时原持有线程已退出同步块,撤销开销小;若还在临界区内,就得等它释放,甚至引发STW
- 默认开启但未必适合你:高并发短生命周期对象(如RPC请求中的临时DTO),频繁撤销反而拖慢性能;可用
-XX:-UseBiasedLocking全局关闭 - 注意:静态方法、类锁(
MyClass.class)默认不启用偏向锁,因为类对象生命周期长、竞争面广,JVM会跳过偏向阶段直接走轻量级锁
轻量级锁为什么叫“轻量”?自旋不是万能解药
轻量级锁本质是“用户态自旋抢锁”:线程把锁记录(Lock Record)压入自己栈帧,再用CAS把对象头指向这个记录。成功=抢到锁;失败=有竞争,进入自旋等待。
但自旋次数有硬限制——默认10次(可通过-XX:PreBlockSpin调),超限立刻升级为重量级锁。这意味着:
- 自旋只适用于“锁持有时间极短”的场景(比如几十纳秒级的计数器更新),否则CPU空转白耗资源
- 多个线程同时自旋?那CAS冲突率飙升,几乎必然超限升级,此时轻量级锁反而比直接上重量级锁更慢
- 自旋期间线程不挂起,仍是RUNNABLE状态——监控看CPU高、线程数多但阻塞少,大概率就是轻量级锁自旋导致的
什么时候会卡在重量级锁?别怪synchronized慢
重量级锁本身不慢,慢的是它背后的代价:线程从RUNNABLE变为BLOCKED,需陷入内核态调用OS Mutex,再经调度器排队唤醒。一次锁争用可能带来微秒级延迟,高并发下雪崩式放大。
真正该排查的是“为何升到这一步”:
- 检查是否真有必要同步:局部变量、ThreadLocal对象、纯计算逻辑,根本不用锁
- 确认锁粒度:是不是整个方法用
synchronized,而实际只需保护某几个字段?考虑拆成细粒度锁或java.util.concurrent原子类 - 留意锁对象复用:多个无关业务共用同一个
Object lock = new Object(),人为制造竞争——每个业务该有自己的锁对象
锁升级过程看着像自动优化,但它的每一步都暴露着代码的真实并发特征。偏爱偏向锁,说明你系统里大量串行访问;频繁走到重量级锁,那得先问自己:这里的竞争真是不可避免的吗?
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










