Java 中 synchronized 锁升级不是为了“让代码跑得更快”,而是通过**按需匹配竞争强度,避免一上来就用高开销机制**,从而在不同并发场景下显著减少不必要的性能损耗。核心逻辑是:能不动操作系统,就别动;能少做一次 CAS,就少做;能不阻塞线程,就不阻塞。
锁升级的本质是“分层防御”策略
JVM 不预设锁的强度,而是从最轻量的状态起步,仅在检测到实际竞争时才逐步加码:
-
无锁 → 偏向锁:对象刚创建或首次被单一线程访问时,JVM 直接把线程 ID 写进对象头 Mark Word,后续同一线程再次进入同步块,只需比对 ID,零 CAS、零内存屏障、无状态切换;
-
偏向锁 → 轻量级锁:当另一线程尝试抢锁,JVM 撤销偏向(需安全点),转为轻量级锁——每个竞争线程在自己栈帧里建一个锁记录,用 CAS 尝试把对象头指向该记录;
-
轻量级锁 → 重量级锁:若自旋多次失败(默认 10 次)或竞争线程数变多,JVM 放弃自旋,把锁膨胀为重量级锁,将线程挂起交由操作系统调度。
每级升级都对应明确的开销削减目标
各级锁解决的是不同层面的资源浪费问题:
-
偏向锁消除“单线程反复加锁”的冗余操作:比如 Spring 单例 Bean 初始化、配置类读取等场景,全程仅一个线程访问,偏向锁让每次进入同步块变成一次指针比对(纳秒级),而非传统锁的原子指令;
-
轻量级锁避免“短暂竞争下的内核态切换”:两个线程交替执行同步块(如简单计数器更新),轻量级锁靠 CPU 自旋等待(几十~几百纳秒),远快于一次用户态→内核态切换(微秒级,约 1–10 μs);
-
重量级锁只在真正高竞争时启用:当自旋已无法快速获得锁(说明等待时间较长),继续空转只会白耗 CPU,此时阻塞线程反而是更节能的选择——把 CPU 让给其他任务,由 OS 在合适时机唤醒。
效率提升的关键不在“锁本身快”,而在“不该用重锁的地方没用”
如果没有锁升级机制,JDK 1.5 及之前版本的 synchronized 一上来就是重量级锁,意味着:
立即学习“Java免费学习笔记(深入)”;
- 哪怕只有一个线程调用
synchronized 方法,也要触发 monitor 创建、线程状态检查、甚至潜在的上下文切换准备; - 所有同步操作都承担了应对“最坏并发”的开销,而现实中绝大多数同步场景并无强竞争;
- 低并发服务(如内部工具类、配置加载)的吞吐量会被无形拖慢,GC 压力也可能间接上升(因 Monitor 对象分配)。
锁升级让 JVM 实现了“按实情付费”:95% 的低竞争同步走偏向/轻量路径,5% 的高竞争场景才兜底到重量级,整体平均延迟大幅下降。
配套优化进一步放大升级收益
锁升级不是孤立机制,它与 JDK 1.6+ 的其他优化协同生效:
-
自适应自旋:JVM 根据历史成功自旋次数动态调整当前自旋轮数,避免固定 10 次导致“该多等没等”或“该停没停”;
-
锁消除:编译器识别出锁对象逃逸不到线程外(如局部 StringBuilder),直接删掉
synchronized; -
锁粗化:连续多个小同步块被合并成一个大块,减少重复加锁/解锁开销。
这些优化共同构成一套“动静结合”的并发治理逻辑:锁升级管运行时状态适配,编译期优化管静态代码结构,合力降低同步成本。