內置鎖(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控制過程相對獨立,但是一個線程可以同時擁有一個或者多個對象的操作權限。