Java synchronized锁优化需按竞争模式动态取舍:偏向锁适合单线程高频重入场景,但撤销成本高;轻量级锁与自旋适用于短时竞争,长等待则浪费CPU。

Java 中 synchronized 的锁优化不是“越优化越好”,而是在特定竞争模式下做动态取舍——它用空间换时间、用复杂度换吞吐、用额外检查换无竞争路径的极简开销。真正关键的,是理解每种优化在什么条件下带来净收益,又在什么场景下反而拖累性能。
偏向锁:单线程高频重入的“免检通道”,但有撤销成本
当一个同步块几乎总被同一个线程反复进入(如对象初始化、配置加载、单例构造),偏向锁能将后续加锁降为一次线程 ID 比较(纳秒级),比轻量级锁的 CAS 还快。但它不是免费的:
- 对象创建时需在 Mark Word 中预留偏向位,内存占用略增;
- 一旦发生竞争(哪怕只一次),JVM 必须执行“偏向撤销”:暂停所有相关线程(safepoint)、遍历该对象的栈帧确认是否持有锁、清空偏向标记并升级为轻量级锁——这个过程可能比直接走轻量级锁还慢;
- 在高并发短生命周期对象场景(如大量 new 出来的 DTO),开启偏向锁反而因频繁撤销导致 STW 时间上升。
轻量级锁与自旋:用 CPU 空转换线程调度开销,但不适用于长等待
轻量级锁把锁竞争从“阻塞唤醒”变为“尝试-自旋-再尝试”,避免了用户态/内核态切换。它的收益取决于竞争持续时间:
- 若临界区执行很快(
- 若临界区本身耗时较长(如含 I/O 或复杂计算),自旋白白消耗 CPU,还可能引发其他线程饥饿;
- 自适应自旋虽会根据历史表现调整次数,但无法预测本次临界区实际耗时——它优化的是“过去”,不是“当下”。
锁粗化与消除:编译器的“善意误判”,依赖逃逸分析精度
锁粗化合并连续同步块、锁消除移除无共享的局部变量同步,这些优化由 JIT 编译器在运行时完成。它们的收益高度依赖逃逸分析结果:
立即学习“Java免费学习笔记(深入)”;
- 若对象逃逸分析不准(如被间接传入未知方法),本该消除的锁会被保留,或本不该粗化的锁被错误合并,导致锁粒度过大、并发度下降;
- 开启逃逸分析(默认开启)本身有编译耗时开销,在启动阶段或小函数中可能得不偿失;
- 锁消除仅对“确定不会逃逸”的局部对象有效,对字段、静态变量、参数对象完全不适用。
重量级锁:兜底机制,代价明确但不可回避
当自旋失败、竞争激烈时,JVM 升级为重量级锁——此时真正调用操作系统互斥量(Mutex)。它的代价清晰:
- 每次获取/释放都触发一次系统调用,涉及用户态→内核态切换,典型耗时在微秒级;
- 线程阻塞后需等待调度器唤醒,上下文切换开销叠加;
- 但它是唯一能保证公平性与强一致性的方案,也是所有优化失效后的安全终点。
本质上,JVM 的锁优化是一套“预测式经济系统”:它预设多数同步是低竞争的,用少量空间和检查成本,换取高频无竞争路径的极致效率;而当预测失败,它宁可承担一次较高代价的升级,也不愿让所有路径都承受重量级锁的恒定开销。开发者要做的,不是禁用或强推某种锁,而是让代码行为匹配优化假设——比如减少锁内耗时、避免跨线程共享短命对象、合理控制锁粒度。


















