synchronized在无/低竞争时性能更优,因轻量级锁和自旋避免内核态切换;ReentrantLock通过AQS队列精细控制线程调度,适合高竞争且需中断、超时等场景。

在高并发场景下,synchronized 和 ReentrantLock 的性能差异,不能只看“谁快谁慢”,关键要看它们在锁竞争不同阶段的底层行为——尤其是用户态与内核态切换开销、以及是否依赖 CAS 自旋等待。JVM 从 JDK 1.6 起对 synchronized 持续优化,本质是不断推迟甚至避免进入重量级锁阶段,而 ReentrantLock 则从设计之初就基于用户态 CAS + AQS 队列,把内核态介入控制得更精细。
轻量级锁与自旋:synchronized 如何减少内核态切换
synchronized 初始加锁走的是“轻量级锁”路径:线程尝试用 CAS 修改对象头中的 Mark Word,成功即获取锁,全程在用户态完成,不涉及任何系统调用或线程挂起。若失败(说明有竞争),JVM 会先尝试“自旋”——让线程在用户态循环重试 CAS,而不是立刻阻塞。自旋成功则避免了上下文切换;自旋失败且竞争持续,才升级为重量级锁(即依赖 OS mutex),触发线程挂起/唤醒,引发用户态→内核态→用户态的完整切换。
这种演进逻辑意味着:
- 无竞争或低竞争时,synchronized 几乎零内核开销,性能极佳
- 中等竞争下,适应性自旋策略(JDK 1.6+)会根据前一次自旋结果动态调整次数,平衡 CPU 占用与响应延迟
- 高竞争长等待场景,一旦膨胀为重量级锁,每次锁获取/释放都伴随昂贵的内核态切换,成为瓶颈
ReentrantLock 的 CAS + AQS 队列机制
ReentrantLock 底层完全基于 AQS(AbstractQueuedSynchronizer),其核心是 volatile state 字段 + CAS 操作 + CLH 同步队列。线程争抢锁时:
- 先用 CAS 尝试修改 state(0→1),成功即获锁,纯用户态,无切换
- CAS 失败后,线程不会盲目自旋,而是被封装为 Node 加入 AQS 的 FIFO 队列,并调用 LockSupport.park() 主动挂起——这一步才触发内核态切换
- 唤醒时由前驱节点调用 unpark(),线程恢复执行并再次尝试获取锁
相比 synchronized 的“被动膨胀”,ReentrantLock 是“主动队列化”:它把线程调度权交还给 JVM 和 OS,避免了无意义的 CPU 空转,也使得中断、超时等高级特性成为可能。
公平性与内核态成本的隐性关联
公平锁模式下,ReentrantLock 严格按队列顺序分配锁,虽避免饥饿,但每次 park/unpark 都需内核介入,吞吐量下降明显。而非公平锁允许新线程插队尝试 CAS,大概率在用户态直接抢到锁,大幅减少内核态切换频次——这也是为什么非公平模式是默认选择。
synchronized 始终是非公平的,且没有用户可配置的公平开关。它的“非公平”体现在:锁释放后,任意等待线程(不一定是最早等待的)都可能被唤醒并立即通过 CAS 抢到锁,这种设计天然倾向减少内核态往返。
锁消除与粗化:JVM 对 synchronized 的智能干预
JIT 编译器能在运行期识别出“锁仅限于单线程作用域”的代码段(如 StringBuilder.append),直接移除 synchronized 字节码(锁消除);也能合并相邻的同步块(锁粗化),减少反复加锁/解锁带来的状态检查和潜在内核切换。这些优化是 ReentrantLock 无法享受的——因为它是 API 层实现,JIT 无法跨方法分析其语义。
这意味着,在大量短生命周期、局部同步的场景中,synchronized 经 JIT 优化后的实际开销,可能远低于表面看起来的“关键字开销”。
不复杂但容易忽略:性能高低从来不是由锁类型本身决定的,而是由“是否发生内核态切换”“切换频率”“是否空转浪费 CPU”共同决定的。理解这一点,才能在 synchronized 的简洁性与 ReentrantLock 的可控性之间做出真正贴合业务的取舍。


















