對象內置鎖ObjectMonitor


內置鎖(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隊列之間的關系轉換:

img

內置鎖狀態轉換圖:

對象內置鎖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隊列后面會說),那就看一下底層的waitnotify方法

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

參考:

Java線程同步與信號量的奧秘

JVM 管程介紹

Java並發基石——所謂“阻塞”:Object Monitor和AQS(1)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM