1.深入JVM源碼,查看如何實現wait(),notify()方法

1.深入JVM源碼,查看如何實現wait()

在java中,每個對象都是Object的子類,java中每個對象都是"鎖",但是這是如何實現的呢,今天我們看看JVM層面如何實現Object.wait/notify方法的。

在講這個之前我們需要了解一下java的對象在內存中的佈局情況;

1.對象頭:下面重點講解
2.實例變量:存放類的屬性數據信息,包括父類的屬性信息,如果是數組的實例部分還包括數組的長度,這部分內存按4字節對齊
3.填充數據:由於虛擬機要求對象起始地址必須是8字節的整數倍。填充數據不是必須存在的,僅僅是爲了字節對齊

1.對象頭

對象頭是實現synchronized以及wait/notify的基礎,一般而言synchronized使用的鎖對象是存儲在Java對象頭裏的,對象頭結構如下:

每個對象頭由兩部分組成,klass pointerMark WordArray length(只有是數組纔會有)

1.Mark Word

32位操作系統:
在這裏插入圖片描述64位操作系統(2bit的鎖標誌標明)
在這裏插入圖片描述
由於對象頭的信息與對象自身定義的數據沒有關係的額外存儲成本,考慮JVM的空間效率,Mark Word 被設計成爲一個非固定的數據結構以便存儲更多有效的數據。

2.klass pointer

Klass Pointer對象指向它的類元數據的指針,虛擬機通過這個指針來確定對象是哪個類的實例。

klass pointer一般佔32個bit即4個字節,如果你有足夠的原因關閉默認的指針壓縮,即啓動參數加上了-XX:-UseCompressedOops那麼它就佔64個bit.

//oopDesc中定於
volatile markOop  _mark //表示MarkWord,但是都是_mark是8字節,疑問請看下面,其實markOopDesc->value()存儲了MarkWord;
  union _metadata {
    Klass*      _klass; //未開啓指針壓縮
    narrowKlass _compressed_klass;  //開啓指針壓縮
  } _metadata;


typedef juint  narrowKlass; //爲4字節,juint==int
typedef class   markOopDesc*   markOop; //64位C++指針爲8字節

MarkWord與markOop大小相同,但是指針指向的不是一個對象麼?我們看一下markOopDesc,

markOop.hpp中註釋的部分,從這裏無法看出它是用哪個字段存儲markWord的,以上MarkWord的圖示可以知道對象頭的8個字節是存儲(是否偏向鎖、鎖標記…等,請看MarkWord的存儲結構),所以不應該是一個指針。

//  32 bits:
//  --------
//             hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
//             JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
//             size:32 ------------------------------------------>| (CMS free block)
//             PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
//  64 bits:
//  --------
//  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
//  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
//  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
//  size:64 ----------------------------------------------------->| (CMS free block)
//
//  unused:25 hash:31 -->| cms_free(與cms垃圾收集器有關):1 age:4    biased_lock(偏向鎖標記):1 lock:2 (COOPs && normal object)
//  JavaThread*:54 epoch:2 cms_free:1 age:4    biased_lock:1 lock:2(無鎖/mark鎖/monitor鎖標記) (COOPs && biased object)
//  narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
//  unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)

所以markOop對象中肯定得有字段存儲MarkWord數據。我們可以看源碼如果獲取鎖標記的。

typedef unsigned long		uintptr_t;
uintptr_t value() const { return (uintptr_t) this; } //將this轉化成無符號的long類型。
bool is_locked()   const {
    return (mask_bits(value(), lock_mask_in_place) != unlocked_value);
  }
  bool is_unlocked() const {
    return (mask_bits(value(), biased_lock_mask_in_place) == unlocked_value);
  }

我們可以看到value()其實就是存儲MarkWord相關信息的,看它的定義正好是long型

typedef unsigned long		uintptr_t;

uintptr_t value() const { return (uintptr_t) this; }

那麼value()就是返回MarkWord信息了。

例如:我們 用ClassLayout來看一下例子

				Object object = new Object();
        System.out.println(object.hashCode());
        System.out.println(ClassLayout.parseInstance(object).toPrintable());

輸出:

1360875712
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 c0 50 1d (00000001 11000000 01010000 00011101) (491831297)
      4     4        (object header)                           51 00 00 00 (01010001 00000000 00000000 00000000) (81)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)

在這裏插入圖片描述
爲什麼這樣存儲,可以參考一下大小端問題。Java採用大端的字節序。

前兩個4字節表示Mark word,第三個四字節表示klass pointer,最後一個4字節是維護8整數倍的填充。

2.JVM如何實現synchronized

1.介紹

輕量級鎖和偏向鎖是Java 6 對synchronized鎖進行優化後新增加的,根據對象頭上標記的鎖來確定當前是什麼鎖。其中每個對象都有monitor,

在HotSpot JVM中,有個ObjectMonitor結構,定義如下:

ObjectMonitor() {
    _header       = NULL;
    _count        = 0;     //_count is approximately |_WaitSet| + |_EntryList|
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;  //表示當前持有monitor的線程
    _WaitSet      = NULL;  //主要存放所有wait的線程的對象,也就是說如果有線程處於wait狀態,將被掛入這個隊列
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ; 
    FreeNext      = NULL ;
    _EntryList    = NULL ; //所有在等待(block)獲取鎖的線程的對象,也就是說如果有線程處於等待獲取鎖的狀態的時候,將被掛入這個隊列。
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
  }

ObjectMonitor中有兩個隊列,_WaitSet、_EntryList分別保存wait線程的對象及獲取鎖的對象列表。
在這裏插入圖片描述
當多個線程同時訪問一段同步代碼,首先會進入_EntryList隊列進行搶佔,當線程獲取到monitor對象後,owner修改爲當前線程並把count+1,若對象調用wait方法,將要釋放持有的monitor,owner=NULL,當前線程進入WaitSet 中等待被喚醒。

monitor對象在每個java對象的對象頭中,synchronized鎖就是用過這種方式實現的,這就是java中的任意對象都可以作爲鎖,同時notify/notifyAll/wait等方法在Object中定義的原因。

2.synchronized的字節碼錶示

public void syncTask(){
       //同步代碼庫
       synchronized (this){
           i++;
       }
   }
//其餘省略...
3: monitorenter  //進入同步方法
//..........省略其他  
15: monitorexit   //退出同步方法

一般有了解的都知道,synchronized是基於monitor實現的,在語句段前加入monitorenter,語句段後加入monitorexit指令,根據ACC_SYNCHRONIZED標記來判別是否添加該指令,這裏的重點不是講解synchronized,感興趣的可以瞭解synchronized由無鎖->偏向鎖->mark鎖->monitor鎖的細節!

3.ObjectWaiter

在瞭解wait之前需要了解 _WaitSet、_EntryList隊列中存儲的到底是什麼 ,看過源碼之後,瞭解到了每個等待的鎖都會被封裝成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
  ObjectWaiter * volatile _prev; //指向上一個ObjectWaiter
  Thread*       _thread; 				 //存儲當前線程
  jlong         _notifier_tid;   //
  ParkEvent *   _event;
  volatile int  _notified ;
  volatile TStates TState ;
  Sorted        _Sorted ;           // List placement disposition
  bool          _active ;           // Contention monitoring is enabled
 public:
  ObjectWaiter(Thread* thread);

  void wait_reenter_begin(ObjectMonitor *mon);
  void wait_reenter_end(ObjectMonitor *mon);
};

wait 方法

// -----------------------------------------------------------------------------
// Wait/Notify/NotifyAll
//
// Note: a subset of changes to ObjectMonitor::wait()
// will need to be replicated in complete_exit above
void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
        Thread *const Self = THREAD;
        assert (Self -> is_Java_thread(),"Must be Java thread!");
        JavaThread * jt = (JavaThread *) THREAD;//把當前線程封裝成JavaThread

        DeferredInitialize();

        // Throw IMSX or IEX.
        CHECK_OWNER();

        EventJavaMonitorWait event;

        // check for a pending interrupt
        if (interruptible && Thread::is_interrupted (Self, true) &&!HAS_PENDING_EXCEPTION){
            // post monitor waited event.  Note that this is past-tense, we are done waiting.
            if (JvmtiExport::should_post_monitor_waited ()){
                // Note: 'false' parameter is passed here because the
                // wait was not timed out due to thread interrupt.
                JvmtiExport::post_monitor_waited (jt,this, false);
                // In this short circuit of the monitor wait protocol, the
                // current thread never drops ownership of the monitor and
                // never gets added to the wait queue so the current thread
                // cannot be made the successor. This means that the
                // JVMTI_EVENT_MONITOR_WAITED event handler cannot accidentally
                // consume an unpark() meant for the ParkEvent associated with
                // this ObjectMonitor.
            }
            if (event.should_commit()) {
                post_monitor_wait_event( & event,0, millis, false);
            }
            TEVENT(Wait - Throw IEX);
            THROW(vmSymbols::java_lang_InterruptedException ());
            return;
        }
				TEVENT(Wait);
        assert (Self -> _Stalled == 0,"invariant");
        Self -> _Stalled = intptr_t(this);
				jt -> set_current_waiting_monitor(this);

        // create a node to be put into the queue
        // Critically, after we reset() the event but prior to park(), we must check
        // for a pending interrupt.
  			//初始化當前線程包裝成ObjectWaiter節點
        ObjectWaiter node (Self);
  			//對應的ObjectWaiter 線程狀態爲wait
        node.TState = ObjectWaiter::TS_WAIT;
				//ParkEvent are used for Java-level "monitor" synchronization. 
  			//ParkEvent 裏的註釋,ParkEvent被使用於java級別的 監視器 同步
        Self -> _ParkEvent ->reset();
        OrderAccess::fence ();          // ST into Event; membar ; LD interrupted-flag
        // Enter the waiting queue, which is a circular doubly linked list in this case
        // but it could be a priority queue or any data structure.
        // _WaitSetLock protects the wait queue.  Normally the wait queue is accessed only
        // by the the owner of the monitor *except* in the case where park()
        // returns because of a timeout of interrupt.  Contention is exceptionally rare
        // so we use a simple spin-lock instead of a heavier-weight blocking lock.
  
        //_WaitSetLock 是ObjectMonitor中的 
  			//volatile int _WaitSetLock;    // protects Wait Queue - simple spinlock 
  			//用這個標記spin對_WaitSet進行加鎖/釋放鎖
  			Thread::SpinAcquire ( & _WaitSetLock,"WaitSet - add");
  			//把node節點添加到_WaitSet(他是一個環形雙向鏈表)
        AddWaiter( & node);
  			//釋放鎖
        Thread::SpinRelease ( & _WaitSetLock);
        if ((SyncFlags & 4) == 0) {
            _Responsible = NULL;
        }
        intptr_t save = _recursions; // record the old recursion count
        _waiters++;                  // wait狀態的線程數,也就是_WaitSet中的數量
        _recursions = 0;             // set the recursion level to be 1 _recursions表示鎖的重入次數
        exit(true, Self);                    // 退出監視器 (monitor)
        guarantee(_owner != Self, "invariant");
        // The thread is on the WaitSet list - now park() it.
  			// 以上就是把線程包裝成 ObjectWaiter並且放入_WaitSet中,下面就需要把線程進行park操作
        // On MP systems it's conceivable that a brief spin before we park
        // could be profitable.
        //
        // TODO-FIXME: change the following logic to a loop of the form
        //   while (!timeout && !interrupted && _notified == 0) park()

        int ret = OS_OK;
        int WasNotified = 0;
        { // State transition wrappers
          	//Self 指向 osthread線程,獲取到系統線程
            OSThread * osthread = Self -> osthread();
          	// 想了解 OsThreadState的各種狀態可以參考下面的狀態列表,在JVM的osThread.hpp中定義了
          	// true 的話,會把線程狀態改爲OBJECT_WAIT,也就是調用Object.wait()方法
          
            OSThreadWaitState osts (osthread, true);
            {
                ThreadBlockInVM tbivm (jt);
                // Thread is in thread_blocked state and oop access is unsafe.
              	// 首先 把javaThread 的狀態置爲 暫停狀態
                jt ->set_suspend_equivalent();
              	// 判斷線程是否中斷等,或者是否發生gc導致的STW HAS_PENDING_EXCEPTION 用來判斷是否gc STW 
                if (interruptible && (Thread::is_interrupted (THREAD, false)||HAS_PENDING_EXCEPTION)){
                // Intentionally empty
            } else
                //_notified 是notify的標誌量,wait的時候會 置爲0 ,notify爲 置爲 1
                if (node._notified == 0) {
                    if (millis <= 0) {
                      	//調用_ParkEvent的park方法,使線程進行park
                        Self -> _ParkEvent ->park();
                    } else {
                        ret = Self -> _ParkEvent -> park(millis);
                    }
                }
                // were we externally suspended while we were waiting?
              	// 我們在等待的時候是否外部暫停 ?
                if (ExitSuspendEquivalent(jt)) {
                    // TODO-FIXME: add -- if succ == Self then succ = null.
                    jt ->java_suspend_self();
                }
            } // Exit thread safepoint: transition _thread_blocked -> _thread_in_vm
          		// 退出線程安全點
            // Node may be on the WaitSet, the EntryList (or cxq), or in transition
            // from the WaitSet to the EntryList.
          	// Node可能在WaitSet 或者EntryList(或者cxq)上,或者從WaitSet->EntryList的過渡
            // See if we need to remove Node from the WaitSet.
          	// 看一看是否我們需要從WaitSet移除節點
            // We use double-checked locking to avoid grabbing _WaitSetLock
          	// 我們使用雙重校驗鎖以避免獲取_WaitSetLock
            // if the thread is not on the wait queue.
            // 如果線程不再wait隊列中
            // Note that we don't need a fence before the fetch of TState.
          	// 抓取TState的時候我們就不需要調用fence()方法(可以參考valtile的底層實現)
            // In the worst case we'll fetch a old-stale value of TS_WAIT previously
          	// 在最壞的情況下,我們抓取了一箇舊的TS_WAIT線程狀態
            // written by the is thread. (perhaps the fetch might even be satisfied
            // by a look-aside into the processor's own store buffer, although given
            // the length of the code path between the prior ST and this load that's
            // highly unlikely).  
          	// 儘管考慮到先前的ST和此加載之間的代碼路徑長度,這可能甚至可以通過留待處理器自己的存儲緩衝區來滿足,但不太可能
            // If the following LD fetches a stale TS_WAIT value
            // then we'll acquire the lock and then re-fetch a fresh TState value.
          	// 我們將要重新獲取鎖,重新抓取線程狀態的值
            // That is, we fail toward safety.
          	// 也就是說,我們無法實現安全。
						
          	//驗證node的線程狀態,我個人的理解是:這樣雙重驗證的話就不需要調用fence()方法
            if (node.TState == ObjectWaiter::TS_WAIT) {
                Thread::SpinAcquire ( & _WaitSetLock,"WaitSet - unlink");
              	//再次驗證node的線程狀態
                if (node.TState == ObjectWaiter::TS_WAIT) {
                    DequeueSpecificWaiter( & node);       // 與WaitSet解除鏈接
                    assert (node._notified == 0,"invariant");
                    node.TState = ObjectWaiter::TS_RUN;
                }
                Thread::SpinRelease ( & _WaitSetLock);
            }

            // The thread is now either on off-list (TS_RUN),
            // on the EntryList (TS_ENTER), or on the cxq (TS_CXQ).
            // The Node's TState variable is stable from the perspective of this thread.
            // No other threads will asynchronously modify TState.
            guarantee(node.TState != ObjectWaiter::TS_WAIT, "invariant");
            
          	OrderAccess::loadload ();
            if (_succ == Self)
                _succ = NULL;
            WasNotified = node._notified;

            //     Reentry phase -- reacquire the monitor.
            //     重入階段,重新獲得監視器
            //     re-enter contended monitor after object.wait().
            //     在object.wait()後重新輸入競爭監視器
            //     retain OBJECT_WAIT state until re-enter successfully completes
            //     保留OBJECT_WAIT狀態,直到重新進入成功完成
            //     Thread state is thread_in_vm and oop access is again safe,
            //     although the raw address of the object may have changed.
            //     (Don't cache naked oops over safepoints, of course).

            // post monitor waited event. Note that this is past-tense, we are done waiting.
            if (JvmtiExport::should_post_monitor_waited ()){
            JvmtiExport::post_monitor_waited (jt,this, ret == OS_TIMEOUT);

            if (node._notified != 0 && _succ == Self) {
              	//個人理解,_notified==1 && _succ == Self 說明當前線程已經調用了notify(),在這裏進行快速的unpark
                // In this part of the monitor wait-notify-reenter protocol it
                // is possible (and normal) for another thread to do a fastpath
                // monitor enter-exit while this thread is still trying to get
                // to the reenter portion of the protocol.
                //
                // The ObjectMonitor was notified and the current thread is
                // the successor which also means that an unpark() has already
                // been done. The JVMTI_EVENT_MONITOR_WAITED event handler can
                // consume the unpark() that was done when the successor was
                // set because the same ParkEvent is shared between Java
                // monitors and JVM/TI RawMonitors (for now).
                //
                // We redo the unpark() to ensure forward progress, i.e., we
                // don't want all pending threads hanging (parked) with none
                // entering the unlocked monitor.
                node._event->unpark();

            }
        }

            if (event.should_commit()) {
                post_monitor_wait_event( & event, node._notifier_tid, millis, ret == OS_TIMEOUT);
            }

            OrderAccess::fence ();

            assert (Self -> _Stalled != 0,"invariant");
            Self ->_Stalled = 0;

            assert (_owner != Self,"invariant");
            ObjectWaiter::TStates v = node.TState;
            if (v == ObjectWaiter::TS_RUN) {
              //如果當前狀態爲TS_RUN 則把當前線程 添加到
                enter(Self);
            } else {
                guarantee(v == ObjectWaiter::TS_ENTER || v == ObjectWaiter::TS_CXQ, "invariant");
                ReenterI(Self, & node);
                node.wait_reenter_end(this);
            }

            // Self has reacquired the lock.
            // Lifecycle - the node representing Self must not appear on any queues.
            // Node is about to go out-of-scope, but even if it were immortal we wouldn't
            // want residual elements associated with this thread left on any lists.
            guarantee(node.TState == ObjectWaiter::TS_RUN, "invariant");
            assert (_owner == Self,"invariant");
            assert (_succ != Self,"invariant");
        } // OSThreadWaitState()

        jt -> set_current_waiting_monitor(NULL);

        guarantee(_recursions == 0, "invariant");
        _recursions = save;     // restore the old recursion count
        _waiters--;             // decrement the number of waiters

        // Verify a few postconditions
        assert (_owner == Self,"invariant");
        assert (_succ != Self,"invariant");
        assert (((oop) (object()))->mark() == markOopDesc::encode (this), "invariant");

        if (SyncFlags & 32) {
            OrderAccess::fence ();
        }

        // check if the notification happened
        if (!WasNotified) {
            // no, it could be timeout or Thread.interrupt() or both
            // check for interrupt event, otherwise it is timeout
            if (interruptible && Thread::is_interrupted (Self, true)
    &&!HAS_PENDING_EXCEPTION){
                TEVENT(Wait - throw IEX from epilog);
                THROW(vmSymbols::java_lang_InterruptedException ());
            }
        }

// NOTE: Spurious wake up will be consider as timeout.
// Monitor notify has precedence over thread interrupt.
    }

總結:wait方法,會把當前線程封裝成ObjectWaiter,加入到waitSet環形雙向鏈表的末尾,後面就通過Self的_ParkEvent的park方法把線程park,park底層實際上調用了pthread_cond_wait方法,後面會進行講解。

threadState:

enum ThreadState {
  ALLOCATED,                    // Memory has been allocated but not initialized
  INITIALIZED,                  // The thread has been initialized but yet started
  RUNNABLE,                     // Has been started and is runnable, but not necessarily 	running
  MONITOR_WAIT,                 // Waiting on a contended monitor lock
  CONDVAR_WAIT,                 // Waiting on a condition variable
  OBJECT_WAIT,                  // Waiting on an Object.wait() call
  BREAKPOINTED,                 // Suspended at breakpoint
  SLEEPING,                     // Thread.sleep()
  ZOMBIE                        // All done, but not reclaimed yet
};

方法將node添加到_WaitSet列表中

/**
 * 添加到_WaitSet 中(環形雙向鏈表)
 * @param node
 */
inline void ObjectMonitor::AddWaiter(ObjectWaiter *node) {

    assert(node != NULL, "should not dequeue NULL node");
    assert(node->_prev == NULL, "node already in list");
    assert(node->_next == NULL, "node already in list");
    // put node at end of queue (circular doubly linked list)
    if (_WaitSet == NULL) {
        //如果_WaitSet爲NULL的話,把node的前後指針分別指向自己
        _WaitSet = node;
        node->_prev = node;
        node->_next = node;
    } else {
        //如果如果_WaitSet不爲NULL的話,把node放在環形隊列末尾,
        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;
    }
}

enter進入monitor的方法

void ATTR ObjectMonitor::enter(TRAPS) {
  
    Thread *const Self = THREAD;
    void *cur;
    //通過CAS嘗試把monitor的_owener設置爲當前線程
    cur = Atomic::cmpxchg_ptr(Self, &_owner, NULL);
  	//enter失敗,return 
    if (cur == NULL) {
        // Either ASSERT _recursions == 0 or explicitly set _recursions = 0.
        assert (_recursions == 0, "invariant");
        assert (_owner == Self, "invariant");
        // CONSIDER: set or assert OwnerIsThread == 1
        return;
    }
		//如果相等就說明,Self發生了重入,重入次數++,return
    if (cur == Self) {
        // TODO-FIXME: check for integer overflow!  BUGID 6557169.
        _recursions++;
        return;
    }
		//如果是當前線程第一次進入該monitor,初始化_recursions,_owner,OwnerIsThread
    if (Self->is_lock_owned((address) cur)) {
        assert (_recursions == 0, "internal state error");
        _recursions = 1;
        // Commute owner from a thread-specific on-stack BasicLockObject address to
        // a full-fledged "Thread *".
        _owner = Self;
        OwnerIsThread = 1;
        return;
    }

    // We've encountered genuine contention.
    assert (Self->_Stalled == 0, "invariant");
    Self->_Stalled = intptr_t(this);

    // Try one round of spinning *before* enqueueing Self
    // and before going through the awkward and expensive state
    // transitions.  The following spin is strictly optional ...
    // Note that if we acquire the monitor from an initial spin
    // we forgo posting JVMTI events and firing DTRACE probes.
    if (Knob_SpinEarly && TrySpin(Self) > 0) {
        assert (_owner == Self, "invariant");
        assert (_recursions == 0, "invariant");
        assert (((oop) (object()))->mark() == markOopDesc::encode(this), "invariant");
        Self->_Stalled = 0;
        return;
    }

    // Prevent deflation at STW-time.  See deflate_idle_monitors() and is_busy().
    // Ensure the object-monitor relationship remains stable while there's contention.
    Atomic::inc_ptr(&_count);

    EventJavaMonitorEnter event;

    { // Change java thread status to indicate blocked on monitor enter.
        JavaThreadBlockedOnMonitorEnterState jtbmes(jt, this);

        DTRACE_MONITOR_PROBE(contended__enter, this, object(), jt);
        if (JvmtiExport::should_post_monitor_contended_enter()) {
            JvmtiExport::post_monitor_contended_enter(jt, this);

            // The current thread does not yet own the monitor and does not
            // yet appear on any queues that would get it made the successor.
            // This means that the JVMTI_EVENT_MONITOR_CONTENDED_ENTER event
            // handler cannot accidentally consume an unpark() meant for the
            // ParkEvent associated with this ObjectMonitor.
        }

        OSThreadContendState osts(Self->osthread());
        ThreadBlockInVM tbivm(jt);

        Self->set_current_pending_monitor(this);

        // TODO-FIXME: change the following for(;;) loop to straight-line code.
      //重點在這裏...............
        for (;;) {
          	//設置java線程爲暫停
            jt->set_suspend_equivalent();
            // cleared by handle_special_suspend_equivalent_condition()
            // or java_suspend_self()
						//重點方法 以上流程如果獲取鎖失敗,則等待鎖的釋放;
            EnterI(THREAD);

            if (!ExitSuspendEquivalent(jt)) break;

            //
            // We have acquired the contended monitor, but while we were
            // waiting another thread suspended us. We don't want to enter
            // the monitor while suspended because that would surprise the
            // thread that suspended us.
            //
            _recursions = 0;
            _succ = NULL;
            exit(false, Self);

            jt->java_suspend_self();
        }
        Self->set_current_pending_monitor(NULL);

        // We cleared the pending monitor info since we've just gotten past
        // the enter-check-for-suspend dance and we now own the monitor free
        // and clear, i.e., it is no longer pending. The ThreadBlockInVM
        // destructor can go to a safepoint at the end of this block. If we
        // do a thread dump during that safepoint, then this thread will show
        // as having "-locked" the monitor, but the OS and java.lang.Thread
        // states will still report that the thread is blocked trying to
        // acquire it.
    }

    Atomic::dec_ptr(&_count);
    assert (_count >= 0, "invariant");
    Self->_Stalled = 0;

    // Must either set _recursions = 0 or ASSERT _recursions == 0.
    assert (_recursions == 0, "invariant");
    assert (_owner == Self, "invariant");
    assert (_succ != Self, "invariant");
    assert (((oop) (object()))->mark() == markOopDesc::encode(this), "invariant");

    // The thread -- now the owner -- is back in vm mode.
    // Report the glorious news via TI,DTrace and jvmstat.
    // The probe effect is non-trivial.  All the reportage occurs
    // while we hold the monitor, increasing the length of the critical
    // section.  Amdahl's parallel speedup law comes vividly into play.
    //
    // Another option might be to aggregate the events (thread local or
    // per-monitor aggregation) and defer reporting until a more opportune
    // time -- such as next time some thread encounters contention but has
    // yet to acquire the lock.  While spinning that thread could
    // spinning we could increment JVMStat counters, etc.

    DTRACE_MONITOR_PROBE(contended__entered, this, object(), jt);
    if (JvmtiExport::should_post_monitor_contended_entered()) {
        JvmtiExport::post_monitor_contended_entered(jt, this);

        // The current thread already owns the monitor and is not going to
        // call park() for the remainder of the monitor enter protocol. So
        // it doesn't matter if the JVMTI_EVENT_MONITOR_CONTENDED_ENTERED
        // event handler consumed an unpark() issued by the thread that
        // just exited the monitor.
    }

    if (event.should_commit()) {
        event.set_klass(((oop) this->object())->klass());
        event.set_previousOwner((TYPE_JAVALANGTHREAD) _previous_owner_tid);
        event.set_address((TYPE_ADDRESS)(uintptr_t)(this->object_addr()));
        event.commit();
    }

    if (ObjectMonitor::_sync_ContendedLockAttempts != NULL) {
        ObjectMonitor::_sync_ContendedLockAttempts->inc();
    }
}

關注公衆號,每週都有新內容

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章