锁消除是JVM在JIT编译阶段通过逃逸分析发现对象未逃逸时自动省略monitorenter/monitorexit指令的优化,字节码保留synchronized但机器码无实际锁操作。

锁消除是 JVM 在运行时自动优化掉“实际上不需要”的 synchronized 同步操作,核心前提是:某个被加锁的对象**不会逃逸出当前线程的作用域**,即它只被单个线程访问,不存在多线程竞争可能。对局部对象做同步时,JVM 通过逃逸分析(Escape Analysis)识别出这一点,就能安全地移除锁开销。
锁消除发生的前提:逃逸分析确认对象不逃逸
逃逸分析是 JIT 编译器在方法调用层级上分析对象引用传播路径的技术。如果一个对象:
- 仅在方法内部创建(如 new StringBuffer()、new Object())
- 没有作为返回值传出
- 没有被赋值给静态字段、成员变量或传入可能跨线程的方法(如 Thread.start()、Executor.submit())
- 没有被发布到其他线程可见的容器(如全局 List、Map、队列等)
那么 JVM 就判定该对象“未逃逸”,其上的 synchronized 操作会被视为冗余,从而触发锁消除。
典型可消除场景:局部 StringBuffer/Vector 的同步方法调用
StringBuffer 的 append()、toString() 等方法都是 synchronized 的,但如果你只在方法内创建并使用它:
立即学习“Java免费学习笔记(深入)”;
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
public String buildMessage() {
StringBuffer sb = new StringBuffer(); // 局部对象,无逃逸
sb.append("Hello");
sb.append(" ");
sb.append("World");
return sb.toString();
}
JVM 会发现 sb 完全局限于当前栈帧,不可能被其他线程看到,于是将每次 append() 内部的锁检查和 monitor 进入/退出操作全部省略。最终执行效果等价于直接使用 StringBuilder —— 没有同步开销,但语义完全安全。
不是所有局部对象都能被消除:注意逃逸信号
即使对象是局部创建的,只要出现以下任一行为,逃逸分析就会失败,锁消除就不会发生:
- 把对象作为参数传给一个可能跨线程调用的方法(例如 submit(runnable)、offer(obj))
- 将对象赋值给 static 字段或 this 的成员变量
- 调用它的 wait()/notify()(这会强制关联 Monitor,阻碍消除)
- 在 synchronized 块中将该对象作为锁,并且该块被内联后仍存在跨方法边界的风险
例如:synchronized(obj) { obj.notify(); } 即使 obj 是局部变量,notify() 明确要求 Monitor 存在,JVM 通常不会消除这个锁。
如何验证锁是否被消除
可通过 JVM 参数开启逃逸分析和锁优化日志(需 Server VM):
-XX:+DoEscapeAnalysis -XX:+EliminateLocks -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintEscapeAnalysis
配合 JMH 基准测试对比 StringBuffer 与 StringBuilder 的吞吐量,若两者性能差异极小(比如

















