1.深入JVM源碼,查看如何實現wait()
在java中,每個對象都是Object的子類,java中每個對象都是"鎖",但是這是如何實現的呢,今天我們看看JVM層面如何實現Object.wait/notify方法的。
在講這個之前我們需要了解一下java的對象在內存中的佈局情況;
1.對象頭:下面重點講解
2.實例變量:存放類的屬性數據信息,包括父類的屬性信息,如果是數組的實例部分還包括數組的長度,這部分內存按4字節對齊
3.填充數據:由於虛擬機要求對象起始地址必須是8字節的整數倍。填充數據不是必須存在的,僅僅是爲了字節對齊
1.對象頭
對象頭是實現synchronized以及wait/notify的基礎,一般而言synchronized使用的鎖對象是存儲在Java對象頭裏的,對象頭結構如下:
每個對象頭由兩部分組成,klass pointer和Mark Word和Array 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();
}
}
關注公衆號,每週都有新內容