内置锁(ObjectMonitor)
Monitor可以理解为一个同步工具或一种同步机制,通常被描述为一个对象。每一个Java对象就有一把看不见的锁,称为内部锁或者Monitor锁。
通常所说的对象的内置锁,是对象头Mark Word中的重量级锁指针指向的monitor对象,该对象是在HotSpot底层C++语言编写的(openjdk里面看),简单看一下代码:
//结构体如下
ObjectMonitor::ObjectMonitor() {
_header = NULL;
_count = 0;
_waiters = 0,
_recursions = 0; //线程的重入次数
_object = NULL;
_owner = NULL; //标识拥有该monitor的线程
_WaitSet = NULL; //等待线程组成的双向循环链表,_WaitSet是第一个节点
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ; //多线程竞争锁进入时的单向链表
FreeNext = NULL ;
_EntryList = NULL ; //_owner从该双向循环链表中唤醒线程结点,_EntryList是第一个节点
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
特别重要的两个属性:
监控区(Entry Set):锁已被其他线程获取,期待获取锁的线程就进入Monitor对象的监控区
待授权区(Wait Set):曾经获取到锁,但是调用了wait方法,线程进入待授权区
ObjectMonitor
队列之间的关系转换:
内置锁状态转换图:
对象内置锁ObjectMonitor流程:
- 所有期待获得锁的线程,在锁已经被其它线程拥有的时候,这些期待获得锁的线程就进入了对象锁的
entry set
区域。 - 所有曾经获得过锁,但是由于其它必要条件不满足而需要wait的时候,线程就进入了对象锁的
wait set
区域 。 - 在
wait set
区域的线程获得Notify/notifyAll
通知的时候,随机的一个Thread(Notify)
或者是全部的Thread(NotifyALL)
从对象锁的wait set
区域进入了entry set
中。 - 在当前拥有锁的线程释放掉锁的时候,处于该对象锁的
entryset
区域的线程都会抢占该锁,但是只能有任意的一个Thread能取得该锁,而其他线程依然在entry set
中等待下次来抢占到锁之后再执行。
既然提到了_waitSet
和_EntryList
(_cxq
队列后面会说),那就看一下底层的wait
和notify
方法
wait方法的实现过程:
//1.调用ObjectSynchronizer::wait方法
void ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
/*省略 */
//2.获得Object的monitor对象(即内置锁)
ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj());
DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), THREAD, millis);
//3.调用monitor的wait方法
monitor->wait(millis, true, THREAD);
/*省略*/
}
//4.在wait方法中调用addWaiter方法
inline void ObjectMonitor::AddWaiter(ObjectWaiter* node) {
/*省略*/
if (_WaitSet == NULL) {
//_WaitSet为null,就初始化_waitSet
_WaitSet = node;
node->_prev = node;
node->_next = node;
} else {
//否则就尾插
ObjectWaiter* head = _WaitSet ;
ObjectWaiter* tail = head->_prev;
assert(tail->_next == head, "invariant check");
tail->_next = node;
head->_prev = node;
node->_next = head;
node->_prev = tail;
}
}
//5.然后在ObjectMonitor::exit释放锁,接着 thread_ParkEvent->park 也就是wait
总结:通过object获得内置锁(objectMonitor),通过内置锁将Thread封装成OjectWaiter对象,然后addWaiter将它插入以_waitSet为首结点的等待线程链表中去,最后释放锁。
notify方法的底层实现
//1.调用ObjectSynchronizer::notify方法
void ObjectSynchronizer::notify(Handle obj, TRAPS) {
/*省略*/
//2.调用ObjectSynchronizer::inflate方法
ObjectSynchronizer::inflate(THREAD, obj())->notify(THREAD);
}
//3.通过inflate方法得到ObjectMonitor对象
ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
/*省略*/
if (mark->has_monitor()) {
ObjectMonitor * inf = mark->monitor() ;
assert (inf->header()->is_neutral(), "invariant");
assert (inf->object() == object, "invariant") ;
assert (ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is inva;lid");
return inf
}
/*省略*/
}
//4.调用ObjectMonitor的notify方法
void ObjectMonitor::notify(TRAPS) {
/*省略*/
//5.调用DequeueWaiter方法移出_waiterSet第一个结点
ObjectWaiter * iterator = DequeueWaiter() ;
//6.后面省略是将上面DequeueWaiter尾插入_EntrySet的操作
/**省略*/
}
总结:通过object
获得内置锁(objectMonitor
),调用内置锁的notify
方法,通过_waitset
结点移出等待链表中的首结点,将它置于_EntrySet
中去,等待获取锁。注意:notifyAll
根据policy
不同可能移入_EntryList
或者_cxq
队列中,此处不详谈。
总结:
每个对象的Object Monitor控制过程相对独立,但是一个线程可以同时拥有一个或者多个对象的操作权限。