Monitor概念
Monitor 被翻译为监视器或管程
每个 Java 对象都可以关联一个 Monitor 对象, 如果使用 synchronized 给对象上锁(重量级)之后,该对象头的 Mark Word 中就被设置指向 Monitor 对象的指针
Monitor 对象其实是操作系统自己产生的,而不是Java运行出来的。
例如:Integer和int关键字,他们所占用的字节分别为8个字节+4个字节,8个字节代表的是Monitor带的Java对象头,能够让操作系统找到并调用。
Java 对象头
普通对象
|----------------------------------------------------------|
| Object Header (64 bits) |
|----------------------------------------------------------|
| Mark Word (32 bits) | Klass Word (32 bits) |
|----------------------------------------------------------|
Mark Word : 用于存储对象自身的运行时数据, 如哈希码 (HashCode) 、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程ID等。同时考虑到虚拟机的空间效率,Mark Word 被设计成一个有着动态定义的数据结构。
Klass Word:类型定义信息 , Java 虚拟机通过这个指针来确定该对象是哪个类的实例(并不是所有的虚拟机都必须在对象数据上保留类型指针)。此外,如果对象是一个 Java 数组,那在对象头中还必须有一块用于记录数组长度的数据,因为如果数组的长度是不确定的,将无法推断出数组的大小。
数组对象
|--------------------------------------------------------------------------------------|
| Object Header (96 bits) |
|--------------------------------------------------------------------------------------|
| Mark Word (32 bits) | Klass Word (32 bits) | array length (32bits) |
|--------------------------------------------------------------------------------------|
其中 Mark Word 结构 (动态数据结构)为
存储内容 | 标志位 | 状态 |
---|---|---|
对象哈希码、对象分代年龄 | 01 | 未锁定 | Normal |
指向锁记录的指针 | 00 | 轻量级锁定 | LightWeight Locked |
指向重量级锁的指针 | 10 | 膨胀(重量级锁定)| HeavyWeight Locked |
空,不需要记录信息 | 11 | GC标记 | Marked for GC |
偏向线程ID、便向时间戳、对象分代年龄 | 01 | 可偏向 | Biased |
32 位虚拟机
|-------------------------------------------------- |-----------------------------------|
| Mark Word (32 bit) | State |
|-------------------------------------------------- |-----------------------------------|
| hashcode:25 | age:4 | biased_block:0 |01 | Normal |
|-------------------------------------------------- |-----------------------------------|
| thread:23 |epoch:2| age:4 | biased_block:1 |01 | Biased |
|-------------------------------------------------- |-----------------------------------|
| ptr_to_lock_record:30 |00 | LightWeight Locked |
|-------------------------------------------------- |-----------------------------------|
| ptr_to_heavyweight_monitor:30 |10 | HeavyWeight Locked |
|-------------------------------------------------- |-----------------------------------|
| |11 | Marked for GC |
|-------------------------------------------------- |-----------------------------------|
64 位虚拟机
|-------------------------------------------------------------|-------------------------|
| Mark Word (64 bit) | State |
|-------------------------------------------------------------|-------------------------|
| unused:25 | hashcode:31 | unused:1 | age:4 | biased_block:0 | 01 | Normal |
|-------------------------------------------------------------|-------------------------|
| thread:23 | epoch:2 | unused:1 | age:4 | biased_block:1 | 01 | Biased |
|-------------------------------------------------------------|-------------------------|
| ptr_to_lock_record:62 | 00 | LightWeight Locked |
|-------------------------------------------------------------|-------------------------|
| ptr_to_heavyweight_monitor:62 | 10 | HeavyWeight Locked |
|-------------------------------------------------------------|-------------------------|
| | 11 | Marked for GC |
|-------------------------------------------------------------|-------------------------|
一个Monitor对象的使用情况:
线程切换
- 刚开始 Monitor 中 Owner 为 null
- 当 Thread-0 执行 synchronized(object) 上锁就会将 Monitor 的所有者 Owner 置为 Thread-0, 同时对象 object 中的 Mark Word 的 state 也会发生相应改变。
- 在 Thread-0 上锁的过程中,如果 Thread-1, Thread-2, Thread-3 也来执行 synchronized(object), 就会进入 EntryList 变成 BLOCKED 状态
- Thread-0 执行完同步代码块的内容,然后唤醒 EntryList 中等待的线程来竞争锁,竞争时是非公平的
- 图中 WaitSet 中的 Thread-4, Thread-5 是之前获得过锁,但条件不满足进入 WAITING 状态的线程,可以由 notifyAll() , notify() 唤醒
注意:
- synchronized 必须是进入同一个对象的 monitor 才有上述的效果
- 不加 synchronized 的对象不会关联监视器,不遵从以上规则
C++源码分析
hpp:头文件的申明 ```cpp // 底层使用的是双向链表 class ObjectWaiter : public StackObj {
ObjectWaiter * volatile _prev; // (指针)指向上一个线程 }ObjectWaiter * volatile _next; // (指针)指向下一个线程(唤醒策略,双向链表,好处是当一个线程优先级较高,从waitSet中移除了,然后指针只要一修改,该线程就被移除了,然后剩下的链表就可以重新连上了)
// 锁的状态 class ObjectMonitor { public: enum { OM_OK, // no error OM_SYSTEM_ERROR, // operating system error OM_ILLEGAL_MONITOR_STATE, // IllegalMonitorStateException OM_INTERRUPTED, // Thread.interrupt() OM_TIMED_OUT // Object.wait() timed out };
//构造器 ObjectMonitor() { _header = NULL; _count = 0; _waiters = 0; _recursions = 0; // 递归;线程的重入次数,典型的System.out.println,嵌套的数量,线程进入一次加一的数量 _object = NULL; // 对应synchronized (object)对应里面的object _owner = NULL; // 标识拥有该monitor的线程 _WaitSet = NULL; // 因为调用object.wait()方法而被阻塞的线程会被放在该队列中 _WaitSetLock = 0 ; // 保护等待队列 自旋锁 _Responsible = NULL; _succ = NULL; _cxq = NULL; // 竞争队列,所有请求锁的线程首先会被放在这个队列中 FreeNext = NULL; _EntryList = NULL; // 阻塞;第二轮竞争锁仍然没有抢到的线程(偏向/可重入) _SpinFreq = 0; _SpinClock = 0; OwnerIsThread = 0; } protected: // protected for jvmtiRawMonitor void volatile _owner; // pointer to owning thread OR BasicLock volatile jlong _previous_owner_tid; // thread id of the previous owner of the monitor volatile intptr_t _recursions; // recursion count, 0 for first entry private: int OwnerIsThread ; // _owner is (Thread ) vs SP/BasicLock ObjectWaiter volatile _cxq ; // LL of recently-arrived threads blocked on entry. // The list is actually composed of WaitNodes, acting // as proxies for Threads. protected: ObjectWaiter volatile _EntryList ; // Threads blocked on entry or reentry. protected: ObjectWaiter * volatile _WaitSet; // LL of threads wait()ing on the monitor private: volatile int _WaitSetLock; // protects Wait Queue - simple spinlock } ```