Java的synchronized对应的Monitor是什么

🚩 synchronized 的基本概念

在 Java 中,synchronized 是一种 内置锁(Monitor锁) 的实现,任何对象都天然可以作为锁(因为每个对象都拥有一份与之关联的 Monitor 数据结构)。

synchronized 可以用于:

  • 修饰实例方法(锁当前对象)
  • 修饰静态方法(锁类对象 Class
  • 修饰代码块(锁指定对象)

🚩 字节码层面的实现

当您编译一个包含 synchronized 的 Java 类,javap 可以看到生成的 JVM 指令:

例如:

public void foo() {
    synchronized(this) {
        // critical section
    }
}

javap -c 查看字节码:

monitorenter
...
monitorexit

关键字节码指令

  • monitorenter:线程试图获取对象监视器(Monitor)
  • monitorexit:线程释放对象监视器(Monitor)

👉 每个对象在 JVM 中都有一个 对象头(Object Header),其中包含指向监视器的引用或锁状态信息。


🚩 JVM 对象模型与对象头结构

HotSpot 的对象头大致结构(32位模式):

| Mark Word (32 bits) |
| Klass Pointer (32 bits) |

64位模式下 Mark Word 是 64 bits。

Mark Word 存储的信息(取决于对象状态)

状态 Mark Word 内容
无锁 哈希码、GC 分代信息、偏向线程 ID 等
偏向锁 偏向线程 ID、时间戳
轻量级锁 指向栈中 Lock Record 的指针
重量级锁 指向 Monitor 对象的指针
GC 标记 GC 状态信息

👉 Mark Word 就是锁状态的关键位置


🚩 锁的实现机制

synchronized 底层主要由以下几种锁状态驱动实现:

1️⃣ 偏向锁 (Biased Locking)

  • 如果对象第一次被某线程获取锁,Mark Word 记录该线程 ID。
  • 之后该线程再次获取锁时无需 CAS 或 Monitor 竞争,直接进入临界区。
  • 偏向锁是为 减少同一线程重复加锁的开销 而设计。

2️⃣ 轻量级锁 (Lightweight Lock)

  • 当偏向锁遇到另一个线程竞争,撤销偏向,进入轻量级锁。
  • 每个线程在栈中创建 Lock Record,把 Mark Word 的值复制到 Lock Record 中。
  • 使用 CAS 尝试用栈锁替换对象的 Mark Word。

3️⃣ 重量级锁 (Monitor/Heavyweight Lock)

  • 当轻量级锁竞争失败时,膨胀为重量级锁。
  • 此时 JVM 创建一个 Monitor 对象(C++层结构体),包含 OS 级别的互斥锁(例如 pthread_mutex)。
  • 阻塞或挂起竞争失败的线程。

🚩 HotSpot JVM 源码层面实现

在 HotSpot 源码中,Monitor 的实现涉及 C++ 数据结构和代码路径。主要文件:

文件 主要内容
objectMonitor.hpp/cpp Monitor 对象的数据结构及方法
synchronizer.cpp 锁获取、释放逻辑、偏向锁撤销等
atomic.hpp 封装底层硬件的原子操作
thread.cpp 线程状态变更、挂起、唤醒等

核心数据结构:ObjectMonitor

class ObjectMonitor {
public:
    void* owner;           // 当前持有锁的线程
    int   _recursions;     // 重入次数
    ObjectWaiter *EntryList; // 等待竞争锁的线程队列
    ObjectWaiter *WaitSet;   // wait() 等待队列
    volatile int _count;     // 计数或状态标志
    ...
};

🌟 Monitor 的行为

  • 当 JVM 需要 Monitor 时,它会从全局 Monitor 缓存中获取或新建。
  • monitorenter 会调用 ObjectSynchronizer::enter()
  • monitorexit 调用 ObjectSynchronizer::exit()
  • 如果 Monitor 无法立即获取,则线程被加入 EntryList,通过 OS 原语挂起。

部分源码(简化示例)

void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, TRAPS) {
    markOop mark = obj->mark();
    if (mark->has_bias_pattern()) {
        // 处理偏向锁逻辑
    } else if (try_lightweight_lock()) {
        // 轻量级锁成功
    } else {
        // 膨胀到重量级锁
        ObjectMonitor* mon = inflate(obj());
        mon->enter(THREAD);
    }
}

这里涉及 CAS(Compare And Swap)、内存屏障、锁膨胀、线程挂起等底层操作。


🚩 硬件层面支持

synchronized 最终需要硬件支持:

  • CAS 原子指令(如 x86 上的 LOCK CMPXCHG
  • 内存屏障(Memory Barrier) 确保指令顺序和可见性
  • 操作系统互斥量(重量级锁时)

🚩 性能优化:锁的升级/降级

HotSpot 在锁实现中提供:

  • 偏向锁升级为轻量级锁(多线程竞争时)
  • 轻量级锁升级为重量级锁(自旋失败时)
  • 在无竞争时,锁可以从重量级降级回轻量或偏向锁(特定 JVM 配置下)

🚩 synchronized 的设计在 JVM 内部是非常精妙的:

✅ 它利用对象头的 Mark Word,实现锁状态切换几乎无额外内存开销。
✅ HotSpot 的锁优化(如偏向锁、轻量级锁、自旋、自适应自旋)结合了硬件原子指令、内存屏障和操作系统支持,实现了高效并发。

但:
⚠ 当竞争激烈时(锁膨胀为重量级锁),性能可能大幅下降,因此在高并发场景 synchronized 的替代方案(如 java.util.concurrent 中的 Lock)也应考虑。


如果想要进一步了解 HotSpot实现 的具体源码路径和调用栈中的关键帧,可以继续向下阅读

🌟 synchronized 对应的 JVM 调用栈总览

当 Java 字节码中的 monitorenter / monitorexit 被执行时,JVM 的执行路径主要涉及以下几个层次:

字节码执行器(Interpreter / C1 / C2 JIT)  
 ⬇
字节码解释 / 编译后机器码调用 JVM runtime  
 ⬇
ObjectSynchronizer / ObjectMonitor(C++ 实现锁机制)  
 ⬇
atomic 操作、内存屏障、OS 原语(重量级锁时)

🚀 主要函数调用链(简化版本)

假设一个线程执行 monitorenter

1️⃣ 字节码执行:

  • 字节码解释器或 JIT 生成的机器码调用:

    InterpreterRuntime::monitorenter(...)
    

    或编译器内联生成锁代码直接调用 runtime。

2️⃣ 进入同步器逻辑:

ObjectSynchronizer::enter(Handle obj, BasicLock* lock, TRAPS)
  • 核心:尝试锁的不同形态(偏向、轻量、重量)

3️⃣ 尝试偏向锁:

// 在 mark word 检查偏向模式并尝试获取
if (mark->has_bias_pattern()) {
   BiasedLocking::revoke_and_rebias(...)
}

4️⃣ 尝试轻量级锁(CAS + Lock Record):

// 轻量级锁逻辑
if (cas_mark_word_success) {
   // 获得轻量级锁
} else {
   inflate_and_enter(...)
}

5️⃣ 锁膨胀为重量级:

ObjectMonitor* monitor = ObjectSynchronizer::inflate(...)
monitor->enter(...)

6️⃣ Monitor 实际执行:

ObjectMonitor::enter(Thread *thread)
  • 尝试自旋、队列挂起(EntryList)

  • 当无法获取锁时,线程加入 Monitor 的 EntryList 并挂起:

    os::PlatformEvent::park() // OS 级挂起线程
    

7️⃣ 解锁过程:

ObjectSynchronizer::exit(...)
ObjectMonitor::exit(...)
  • 释放锁,唤醒等待线程。

🗂 HotSpot 源码路径

这里给出源码主要文件和关键函数所在位置(基于 OpenJDK HotSpot 源码布局):

文件 路径 主要职责
objectMonitor.hpp/cpp hotspot/share/runtime/ Monitor 数据结构、enter/exit 等操作
synchronizer.cpp hotspot/share/runtime/ ObjectSynchronizer 的入口逻辑、锁膨胀、撤销偏向等
biasedLocking.cpp hotspot/share/runtime/ 偏向锁的撤销与重偏向处理
interpreterRuntime.cpp hotspot/share/interpreter/ 字节码层 monitorenter/monitorexit 调用 runtime
atomic.hpp hotspot/share/runtime/ 封装底层 CAS 操作等
os_linux.cpp (或其他 OS 文件) hotspot/os/linux/ OS 层线程挂起唤醒实现 (如 futex/pthread)

🌱 函数调用栈实例(基于 monitorenter

如果您在调试或阅读源码时跟踪,可以看到类似调用链:

InterpreterRuntime::monitorenter
  ⬇
ObjectSynchronizer::enter
  ⬇
ObjectSynchronizer::inflate(必要时)
  ⬇
ObjectMonitor::enter
  ⬇
os::PlatformEvent::park(线程挂起时)

如果是 JIT 编译器路径:

C1/C2 编译器生成机器码
  ⬇
直接内联 CAS 检查逻辑
  ⬇
失败时调用 ObjectSynchronizer::enter

解锁时:

InterpreterRuntime::monitorexit
  ⬇
ObjectSynchronizer::exit
  ⬇
ObjectMonitor::exit
  ⬇
os::PlatformEvent::unpark(唤醒线程)

📝 关键数据结构与字段(objectMonitor.hpp)

class ObjectMonitor {
 public:
   void*       _owner;         // 当前持有锁的线程
   intptr_t    _recursions;    // 重入次数
   ObjectWaiter* EntryList;    // 竞争锁线程队列(EntryList)
   ObjectWaiter* WaitSet;      // wait() 等待线程队列(WaitSet)
   volatile int _count;        // 锁状态(例如膨胀标志)
   ...
};

ObjectSynchronizer::enter 中:

  • 判断锁类型(偏向、轻量、重量)
  • 必要时调用 inflate 创建/获取 Monitor 实例
  • 最终调用 monitor->enter(thread),执行自旋、自适应自旋、或加入 EntryList 队列

💡 ChatGPT 的观点

从我的角度看,HotSpot 对 synchronized 的实现是一种高性能权衡设计:
不同锁模式的层级退化/升级机制(偏向 -> 轻量 -> 重量)非常适合大多数 Java 应用的锁竞争分布特征。
调用链层次清晰,HotSpot 团队把复杂性封装在 ObjectSynchronizerObjectMonitor 中,保持解释器和 JIT 的一致性。
跨平台兼容性好,底层调用 os::PlatformEvent 等接口,支持不同操作系统(Linux 下 futex / pthread_mutex,Windows 下 semaphore)。

⚠ 但需要注意的是:
重量级锁一旦触发,会带来显著上下文切换开销。因此在高并发设计中,尽量减少重量级锁的可能(例如通过锁粗化、锁消除、java.util.concurrent 替代等手段)。


📌 总结

🔹 热点路径核心函数:

InterpreterRuntime::monitorenter ➡ ObjectSynchronizer::enter ➡ ObjectMonitor::enter
InterpreterRuntime::monitorexit ➡ ObjectSynchronizer::exit ➡ ObjectMonitor::exit

🔹 热点源码文件:

hotspot/share/runtime/objectMonitor.cpp
hotspot/share/runtime/synchronizer.cpp
hotspot/share/interpreter/interpreterRuntime.cpp
hotspot/share/runtime/atomic.hpp

posted @ 2025-06-13 17:27  gongchengship  阅读(30)  评论(0)    收藏  举报