synchronized 锁消除是什么
锁消除是 java hotspot 虚拟机在即时编译(jit)阶段采用的一种同步省略技术,其核心目的是移除那些本不该存在的重量级锁以提升性能。当 jit 编译器通过逃逸分析发现某个对象仅在当前线程或方法内使用,且不会逃逸到外部时,就会判定该处的 synchronized 块是不必要的,从而直接将其消除。这样做不仅避免了加锁和解锁带来的系统开销,还让代码执行效率接近于无同步操作的状态。

如何触发锁消除机制
要触发锁消除,开发者需要编写一段包含局部变量同步的代码,并确保这些变量没有发生逃逸。在默认情况下,HotSpot 虚拟机通常会自动开启逃逸分析和锁消除功能,因此你不需要手动配置参数,只需关注代码逻辑即可。只要对象的生命周期被严格限制在方法栈帧内部,JVM 就能识别出这种“不安全的”但实际安全的同步行为并予以优化。

public class LockEliminationDemo {
public static void main(String[] args) {
long start = System.currentTimeMillis();
// 循环执行以激发 JIT 编译
for (int i = 0; i < 10000000; i++) {
concatString("Hello", "World");
}
long end = System.currentTimeMillis();
System.out.println("Time taken: " + (end - start) + " ms");
}
public static String concatString(String s1, String s2) {
StringBuilder sb = new StringBuilder();
// 这里的 synchronized 会被 JIT 消除
synchronized (sb) {
sb.append(s1);
sb.append(s2);
}
return sb.toString();
}
}运行上述代码时,虽然我们在 concatString 方法中对局部变量 sb 使用了 synchronized,但由于 sb 是局部变量且未返回或泄露给其他线程,JIT 编译器会将其识别为可消除的锁。最终生成的机器码中不包含任何锁指令,从而实现了极高的执行速度。
立即学习“Java免费学习笔记(深入)”;
锁消除对 StringBuilder 的影响
在 Java 开发中,最典型的锁消除应用场景就是 StringBuilder 的使用。由于 StringBuilder 本身是非线程安全的,但在单线程环境下进行字符串拼接时,我们往往习惯性地对其加锁以保证安全,或者在某些旧版本代码中为了兼容而保留锁。锁消除机制能够精准地识别出这种“过度保护”,并将其彻底移除。

// 对比:如果 sb 逃逸了,锁就不会被消除
public class EscapingObject {
private static StringBuilder sharedSb = new StringBuilder();
public static void unsafeAppend() {
StringBuilder sb = new StringBuilder();
synchronized (sb) { // 注意:这里锁的是局部变量 sb,不是 sharedSb
sb.append("test");
}
sharedSb.append(sb); // sb 的内容被共享,但 sb 对象本身仍可能被优化
}
}在上述代码中,即使我们将 sb 的内容追加到了静态变量 sharedSb 中,只要 sb 对象本身没有作为引用返回或被其他线程直接访问,JIT 依然可能对其内部的同步块进行优化。然而,如果我们将 sb 直接赋值给静态变量或返回它,即发生了逃逸,那么 synchronized 锁将不会被消除,因为此时该对象确实需要在线程间保持可见性和原子性。理解这一点有助于开发者在编写高性能代码时,合理设计对象的作用域,避免不必要的同步开销。


















