JUC--Monitor锁

JUC–Monitor锁

背景

  • JUC–Monitor锁

  • 博主以黑马JUC进行学习

Java对象头

  • Java 对象在堆内存中分为 3 部分:对象头(Header)实例数据(Instance Data)对齐填充(Padding)。其中对象头是实现 Monitor、锁、GC 的关键,不同状态下(普通对象 / 数组对象、是否加锁)结构不同。

    • 普通对象头(64位JVM)
    区域 大小(64 位) 核心作用
    Mark Word(标记字段) 8 字节 存储对象的哈希值、GC 年龄、锁状态、Monitor 指针、偏向锁线程 ID 等核心信息
    Klass Pointer(类型指针) 8 字节(默认) 指向对象所属类的元数据(klass),JVM 通过它确定对象的类型(可通过 -XX:+UseCompressedOops 压缩为 4 字节)
    • 数组对象的对象头
    区域 大小(64 位) 作用
    Mark Word 8 字节 同普通对象
    Klass Pointer 8 字节 同普通对象
    Array Length 4 字节 存储数组的长度
    • Mark Word 的核心状态
    锁状态 2bit 锁标志位 Mark Word 存储内容
    无锁 01 对象哈希值(31bit) + 分代年龄(4bit) + 是否偏向锁(1bit:0) + 锁标志位(2bit:01)
    偏向锁 01 偏向锁线程 ID(54bit) + Epoch(2bit) + 分代年龄(4bit) + 是否偏向锁(1bit:1) + 锁标志位(2bit:01)
    轻量级锁 00 指向栈中锁记录的指针(62bit) + 锁标志位(2bit:00)
    重量级锁(Monitor) 10 指向 Monitor 对象的指针(62bit) + 锁标志位(2bit:10)
    GC 标记 11 空(仅锁标志位 11,用于 GC 标记)

Monitor(锁)

  • Monitor 被翻译为监视器管程
  • 每个Java 对象都可以关联一个Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的Mark Word 中就被设置指向 Monitor 对象的指针
  • 结构
1
2
3
4
5
6
7
8
9
// JVM 底层用 C++ 实现的 ObjectMonitor 核心结构
struct ObjectMonitor {
_header _header; // 基础头信息
void* _owner; // 指向持有当前锁的线程(NULL 表示无线程持有)
_WaitSet _WaitSet; // 等待锁的线程队列(调用 wait() 后进入)
_EntryList _EntryList; // 等待获取锁的线程队列(阻塞的线程)
int _count; // 锁的重入次数
// 其他辅助字段...
};
  • 例子

    • 刚开始 Monitor中的Owner为null

    • 当Thread-2执行 synchronized(obj)就会将Monitor的所有者Owner置为Thread-2,Monitor中只能有一个Owner

    • 当Thread-2上锁的过程中,如果Thread-3,Thread-4,Thread-5 也来执行 synchronized(obj),就会进入EntryList BLOCKED

    • Thread-2执行完同步代码块的内容,然后唤醒EntryList中等待的线程来竞争锁,竞争是非公平的

      注意

      • synchronized 必须是进入同一个对象的 monitor 才有上述的效果
      • 不加 synchronized 的对象不会关联监视器,不遵从以上规则