1、介绍
Monitor是在JVM层对Java并发控制synchronized的重量级锁的实现。通过ObjectMonitor来实现并发的锁控制。同时也是Java基础对象Object的wait,nofity方法的底层支持实现。
2、对象模型
2.1 数据结构图
ObjectMonitor整体上可以分为两部分,一部分是是这个监控对象的基本信息表示当前锁的实时状态,一部分表示各种情况下需要获取锁的排队信息。如下图所示:
基本信息
1 header
用来保存锁对象的mark word的值。因为object里面已经不保存mark word的原来的值了,保存的是ObjectMonitor对象的地址信息。当所有线程都完成了之后,需要销毁掉ObjectMonitor的时候需要将原有的header里面的值重新复制到mark word中来。
2 object
指向的是对象的地址信息,方便通过ObjectMonitor来访问对应的锁对象。
3 owner
指向的是当前获得线程的地址,用来判断当前锁是被哪个线程持有。
排队信息
1cxq
线程刚进来的时候没有获取到锁的时候,在当前的排队队列。
2waitSet
是指已经获取得一次锁了,对象调用了wait方法,讲当前线程挂起了就进入了等待队列。等待时间到期的时候唤醒,或者其他线程唤醒。
3 entryList
是队列用来获取锁的缓冲区,用来将cxq和waitSet中的数据 移动到entryList进行排队。这个统一获取锁的入口。一般是cxq 或者waitSet数据复制过来进行统一排队。
2.2 ObjectMonitor 的源代码
ObjectMonitor() {
_header = NULL;
_count = 0;
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL;
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
//多线程竞争锁进入时的单向链表
_cxq = NULL ;
FreeNext = NULL ;
//_owner从该双向循环链表中唤醒线程结点,_EntryList是第一个节点
_EntryList = NULL ;
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
_previous_owner_tid = 0;
}
2.3 ObjectWaiter源代码
下面是排队对象ObjectWaiter的源代码的信息
class ObjectWaiter : public StackObj {
public:
enum TStates { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER, TS_CXQ } ;
enum Sorted { PREPEND, APPEND, SORTED } ;
ObjectWaiter * volatile _next;
ObjectWaiter * volatile _prev;
//等待的线程
Thread* _thread;
jlong _notifier_tid;
ParkEvent * _event;
volatile int _notified ;
volatile TStates TState ;
Sorted _Sorted ; // List placement disposition
bool _active ; // Contention monitoring is enabled
};
3、核心方法
3.1 TryLock
int ObjectMonitor::TryLock (Thread * Self) {
for (;;) {
void * own = _owner ;
//已经有线程获取到锁了 直接返回
if (own != NULL){
return 0 ;
}
//获取锁成功
if (Atomic::cmpxchg_ptr (Self, &_owner, NULL) == NULL) {
return 1 ;
}
if (true) return -1 ;
}
}
通过上面代码我们可以详细的获取锁的逻辑,判断当前的监视器对象是否已经为空了,如果不为空说明已经有线程拿到锁了直接结束。当为空闲的尝试设置当前线程来获取锁,成功了直接返回1 。通过上面代码看总体上只做一次尝试,并不会多次尝试。
3.3 try_enter
try_enter 的方法是尝试获取当前锁对象 ,在已经拿过一次锁对象了之后,二次重新进入锁的场景使用。整个过程是只做一次尝试,不论成功失败都只执行一次。