synchronized底层不直接依赖cpu的lock指令,而是基于jvm的monitor机制和对象头mark word状态变更,通过monitorenter/monitorexit指令触发锁升级(偏向→轻量→重量级),轻量级锁阶段的cas可能隐含lock指令,但内存可见性主要由jmm的happens-before规则和内存屏障保障。

Java 的 synchronized 在 CPU 层面**并不直接生成 LOCK 前缀指令**,这是常见的误解。它底层依赖的是 JVM 的 Monitor 机制和对象头(Mark Word)状态变更,而非 x86 汇编中的 LOCK 指令。真正用到 LOCK 前缀的,是 java.util.concurrent 包中基于 CAS 的锁实现(比如 ReentrantLock 的 AQS 队列操作),而不是 synchronized。
为什么 synchronized 不靠 LOCK 指令?
synchronized 是 JVM 级别的同步原语,其加锁/解锁行为由 HotSpot 解释器或 JIT 编译器在运行时动态处理:
- 字节码层面只插入
monitorenter和monitorexit指令,JVM 根据锁状态(偏向锁 → 轻量级锁 → 重量级锁)选择不同路径; - 在轻量级锁阶段,JVM 会使用 CAS(Compare-And-Swap)尝试更新对象头的 Mark Word —— 这时底层确实可能触发
LOCK CMPXCHG等带 LOCK 前缀的原子指令; - 但该 CAS 仅用于“尝试获取锁”的瞬间,并非整个同步块执行期间持续占用总线或内存屏障;
- 一旦升级为重量级锁,线程阻塞/唤醒交由操作系统完成,此时依赖的是内核态互斥量(如 pthread_mutex),与 CPU 的 LOCK 指令无直接关系。
真正用到 LOCK 前缀的是哪些场景?
LOCK 前缀的作用是确保多核 CPU 下对内存地址的读-改-写操作具有原子性(如 inc, add, cmpxchg)。在 Java 并发中,它出现在:
-
Unsafe.compareAndSwapInt()等底层方法调用时,JIT 编译后常映射为LOCK CMPXCHG; - AQS 中
state的修改(如acquire/release)、CLH 队列节点的入队/出队; -
AtomicInteger.incrementAndGet()等原子类的 CAS 操作; - 显式锁(如
ReentrantLock)在争抢锁失败时的自旋重试逻辑中。
那 synchronized 的内存可见性靠什么保证?
它不靠 LOCK 指令,而是靠 JVM 规范定义的 **happens-before 规则 + 内存屏障**:
- 退出
synchronized块时,JVM 插入 StoreStore 和 StoreLoad 屏障,强制将本地缓存写回主内存; - 进入
synchronized块时,插入 LoadLoad 和 LoadStore 屏障,清空本地工作内存,后续读取必须从主内存加载; - 这些屏障在 x86 上可能编译为
mfence或隐含在lock指令中,但属于 JVM 实现细节,开发者无需也不应假设具体指令。
简单对比:synchronized vs CAS 锁的硬件依赖
(注意:这不是性能优劣判断,而是机制差异)
- synchronized:锁状态存在对象头里,CPU 层面主要靠 JVM 管理线程调度 + 必要时的 CAS(仅轻量级锁阶段);
- ReentrantLock / AtomicInteger:全程依赖 Unsafe 的 CAS,而 CAS 在 x86 上必然编译为带 LOCK 前缀的指令,属于“用户态原子操作”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











