解构`synchronized`背后的重量级锁

`synchronized`是Java并发的基石,但它的底层实现远比表面看起来要复杂。它并非一个静态的“重量级”锁,而是一个根据竞争激烈程度动态演进的智能系统。本应用将通过交互式可视化的方式,带您深入探索从锁升级到`ObjectMonitor`的完整旅程。

第一站:锁的升级之旅

`synchronized`的性能奥秘在于其锁升级机制。JVM会乐观地从最低成本的锁开始,只有在竞争出现时才逐步“膨胀”为更重的锁。点击下方流程中的各个阶段,查看其详细机制和在对象头(Mark Word)中的表示。

无锁

偏向锁

轻量级锁

重量级锁

无锁状态

对象刚创建时,没有任何线程锁定它。Mark Word中的锁标志位为'01',偏向锁位为'0'。此时,它存储的是对象的哈希码和GC分代年龄等信息。

Mark Word (64位):

...unused... | hashCode | age | 001 | 0

第二站:深入监视器(`ObjectMonitor`)内部

当锁膨胀为重量级锁后,JVM会为其关联一个原生的`ObjectMonitor`对象。它像一个调度中心,管理着所有竞争和等待该锁的线程。通过下面的按钮模拟线程操作,观察`ObjectMonitor`内部的变化。

模拟操作台

操作日志:

点击按钮开始模拟...

ObjectMonitor

锁持有者 (`_owner`)

NULL

重入计数 (`_recursions`)

0

竞争队列 (`_EntryList`)

等待集 (`_WaitSet`)

第三站:重量级锁的“重量”何在?

重量级锁的代价主要源于线程阻塞时,需要从“用户态”切换到“内核态”,并由操作系统调度。这个过程称为“上下文切换”,其开销远大于轻量级锁在用户态执行的“CAS”操作。缓存失效是其最主要的性能惩罚。

第四站:现代抉择 `synchronized` vs. `ReentrantLock`

过去`ReentrantLock`性能普遍更优,但随着JVM对`synchronized`的深度优化,二者性能已不相上下。如今,选择的关键在于功能,而非性能臆断。点击特性进行比较。

`synchronized` (内置锁)

  • 公平性: 非公平
  • 尝试获取锁: 不支持
  • 可中断等待: 不支持
  • 条件变量: 仅一个 (wait/notify)

`ReentrantLock` (API锁)

  • 公平性: 可配置 (默认非公平)
  • 尝试获取锁: 支持 (tryLock)
  • 可中断等待: 支持 (lockInterruptibly)
  • 条件变量: 可创建多个Condition对象

点击上面任意特性,查看详细说明。

终点站:核心建议与实践指南

理解了底层原理后,我们能做出更明智的并发编程决策。以下是基于现代JVM特性的核心实践建议。

1. 默认首选`synchronized`

对于绝大多数场景,`synchronized`因其简洁、安全且经过深度优化的特性,是最佳的默认选择。

2. 为功能选择`ReentrantLock`

仅在确实需要公平性、定时/可中断锁、多条件变量等高级功能时,才使用`ReentrantLock`。

3. 靠剖析而非猜测

不要基于过时的传言做性能判断。遇到瓶颈时,使用Profiler工具进行实际测量。

4. 减少竞争是根本

优化锁性能的最佳方式是减少锁竞争,例如使用并发集合、缩短临界区等。最快的锁是不用抢的锁。