锁消除是jvm通过逃逸分析识别非逃逸对象后自动移除其同步锁的优化技术;非逃逸对象仅在当前线程内创建使用,无共享风险,如方法内新建的stringbuilder,其append的synchronized会被jit抹去,避免monitor开销。

同步消除(也叫锁消除)不是手动去掉 synchronized,而是 JVM 在运行时自动识别出“根本不会被多线程共享”的对象,然后把加在它身上的锁直接删掉。
非逃逸对象是锁消除的前提
一个对象如果只在当前方法或当前线程内创建、使用、销毁,从不传递给其他线程,也不作为返回值、静态字段或成员变量长期持有,JVM 就判定它“未逃逸”。这类对象天然线程私有,不存在竞争可能。
比如下面这段代码:
public String concat(String a, String b) {
StringBuilder sb = new StringBuilder();
sb.append(a).append(b);
return sb.toString();
}
这里的 StringBuilder sb 是典型的非逃逸对象:它在方法内新建,仅被当前线程调用,方法结束即不可达。虽然 StringBuilder 的 append 方法内部用了 synchronized,但 JIT 编译器通过逃逸分析确认它不会被共享,就会把那些锁操作全部抹掉——效果等同于直接用非同步的 StringBuffer 或手动拼接。
为什么消除这些锁能提升性能
哪怕只是空的 synchronized 块,也会触发 monitor 进入/退出逻辑,涉及对象头 Mark Word 修改、可能的 CAS 操作、甚至线程状态切换开销。对高频调用的小对象操作(如字符串拼接、临时集合构建),累积起来影响明显。
- 没有锁竞争 ≠ 没有锁开销;只要写了 synchronized,JVM 默认就要走完整锁流程
- 锁消除后,字节码里直接看不到 monitorenter/monitorexit 指令
- 该优化完全由 JIT 在运行期完成,开发者无需改写逻辑,只需写出局部、短生命周期的对象使用习惯
哪些情况容易触发锁消除
以下写法更容易被 JIT 识别为非逃逸,从而启用锁消除:
- 在方法内部 new 出来的对象,且只在本方法内调用其同步方法
- 对象作为局部变量,未赋值给任何实例字段、静态字段或传入其他线程可见的容器(如 ConcurrentHashMap、static List)
- 未将对象 this 或其引用作为参数传递给可能跨线程调用的方法(如 Thread.start()、Executor.submit())
反之,只要有一处可能导致对象“逃出”当前线程作用域,JVM 就不敢消除锁——宁可保守保留,也不冒险破坏线程安全。










