jdk源碼剖析二: 對象內存佈局、synchronized終極原理

 不少人一提到鎖,天然第一個想到了synchronized,但一直不懂源碼實現,現特意追蹤到C++層來剝開synchronized的面紗。java

網上的不少描述大都不全,讓人看了不夠爽,看完本章,你將完全瞭解synchronized的核心原理。node

1、啓蒙知識預熱

開啓本文以前先介紹2個概念react

1.1.cas操做

爲了提升性能,JVM不少操做都依賴CAS實現,一種樂觀鎖的實現。本文鎖優化中大量用到了CAS,故有必要先分析一下CAS的實現。算法

CAS:Compare and Swap。windows

JNI來完成CPU指令的操做:數組

unsafe.compareAndSwapInt(this, valueOffset, expect, update);安全

CAS有3個操做數,內存值V,舊的預期值A,要修改的新值B。若是A=V,那麼把B賦值給V,返回V;若是A!=V,直接返回V。網絡

打開源碼:openjdk\hotspot\src\oscpu\windowsx86\vm\ atomicwindowsx86.inline.hpp,以下圖:0數據結構

os::is_MP()  這個是runtime/os.hpp,實際就是返回是否多處理器,源碼以下:多線程

 

如上面源代碼所示(看第一個int參數便可),LOCK_IF_MP:會根據當前處理器的類型來決定是否爲cmpxchg指令添加lock前綴。若是程序是在多處理器上運行,就爲cmpxchg指令加上lock前綴(lock cmpxchg)。反之,若是程序是在單處理器上運行,就省略lock前綴(單處理器自身會維護單處理器內的順序一致性,不須要lock前綴提供的內存屏障效果)。

 

1.2.對象頭

HotSpot虛擬機中,對象在內存中存儲的佈局能夠分爲三塊區域:對象頭(Header)、實例數據(Instance Data)和對齊填充(Padding)。

HotSpot虛擬機的對象頭(Object Header)包括兩部分信息:

第一部分"Mark Word":用於存儲對象自身的運行時數據, 如哈希碼(HashCode)、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等等.

第二部分"Klass Pointer":對象指向它的類的元數據的指針,虛擬機經過這個指針來肯定這個對象是哪一個類的實例。(數組,對象頭中還必須有一塊用於記錄數組長度的數據,由於虛擬機能夠經過普通Java對象的元數據信息肯定Java對象的大小,可是從數組的元數據中沒法肯定數組的大小。 )

32位的HotSpot虛擬機對象頭存儲結構:(下圖摘自網絡)

                                                              圖1 32位的HotSpot虛擬機對象頭Mark Word組成

爲了證明上圖的正確性,這裏咱們看openJDK--》hotspot源碼markOop.hpp,虛擬機對象頭存儲結構:

                                             圖2 HotSpot源碼markOop.hpp中註釋

單詞解釋:

hash: 保存對象的哈希碼
age: 保存對象的分代年齡
biased_lock: 偏向鎖標識位
lock: 鎖狀態標識位
JavaThread*: 保存持有偏向鎖的線程ID
epoch: 保存偏向時間戳

上圖中有源碼中對鎖標誌位這樣枚舉

1 enum {   locked_value             = 0,//00 輕量級鎖
2          unlocked_value           = 1,//01 無鎖
3          monitor_value            = 2,//10 監視器鎖,也叫膨脹鎖,也叫重量級鎖
4          marked_value             = 3,//11 GC標記
5          biased_lock_pattern      = 5 //101 偏向鎖
6   };

下面是源碼註釋:

                                        圖3 HotSpot源碼markOop.hpp中鎖標誌位註釋

看圖3,不論是32/64位JVM,都是1bit偏向鎖+2bit鎖標誌位。上面紅框是偏向鎖(第一行是指向線程的顯示偏向鎖,第二行是匿名偏向鎖)對應枚舉biased_lock_pattern,下面紅框是輕量級鎖、無鎖、監視器鎖、GC標記,分別對應上面的前4種枚舉。咱們甚至能看見鎖標誌11時,是GC的markSweep(標記清除算法)使用的。(這裏就再也不拓展了)

對象頭中的Mark Word,synchronized源碼實現就用了Mark Word來標識對象加鎖狀態。

2、JVM中synchronized鎖實現原理(優化)

你們都知道java中鎖synchronized性能較差,線程會阻塞。本節將以圖文形式來描述JVM的synchronized鎖優化。

在jdk1.6中對鎖的實現引入了大量的優化來減小鎖操做的開銷:

鎖粗化(Lock Coarsening):將多個連續的鎖擴展成一個範圍更大的鎖,用以減小頻繁互斥同步致使的性能損耗。

鎖消除(Lock Elimination):JVM及時編譯器在運行時,經過逃逸分析,若是判斷一段代碼中,堆上的全部數據不會逃逸出去歷來被其餘線程訪問到,就能夠去除這些鎖。

輕量級鎖(Lightweight Locking):JDK1.6引入。在沒有多線程競爭的狀況下避免重量級互斥鎖,只須要依靠一條CAS原子指令就能夠完成鎖的獲取及釋放。

偏向鎖(Biased Locking):JDK1.6引入。目的是消除數據再無競爭狀況下的同步原語。使用CAS記錄獲取它的線程。下一次同一個線程進入則偏向該線程,無需任何同步操做。

適應性自旋(Adaptive Spinning):爲了不線程頻繁掛起、恢復的狀態切換消耗。產生了忙循環(循環時間固定),即自旋。JDK1.6引入了自適應自旋。自旋時間根據以前鎖自旋時間和線程狀態,動態變化,用以指望能減小阻塞的時間。

 鎖升級:偏向鎖--》輕量級鎖--》重量級鎖

2.1.偏向鎖

  按照以前的HotSpot設計,每次加鎖/解鎖都會涉及到一些CAS操做(好比對等待隊列的CAS操做),CAS操做會延遲本地調用,所以偏向鎖的想法是一旦線程第一次得到了監視對象,以後讓監視對象「偏向」這個線程,以後的屢次調用則能夠避免CAS操做。
  簡單的講,就是在鎖對象的對象頭(開篇講的對象頭數據存儲結構)中有個ThreaddId字段,這個字段若是是空的,第一次獲取鎖的時候,就將自身的ThreadId寫入到鎖的ThreadId字段內,將鎖頭內的是否偏向鎖的狀態位置1.這樣下次獲取鎖的時候,直接檢查ThreadId是否和自身線程Id一致,若是一致,則認爲當前線程已經獲取了鎖,所以不需再次獲取鎖,略過了輕量級鎖和重量級鎖的加鎖階段。提升了效率。
注意:當鎖有競爭關係的時候,須要解除偏向鎖,進入輕量級鎖。

每個線程在準備獲取共享資源時:

第一步,檢查MarkWord裏面是否是放的本身的ThreadId ,若是是,表示當前線程是處於 「偏向鎖」.跳太輕量級鎖直接執行同步體。

得到偏向鎖以下圖:

 

2.2.輕量級鎖和重量級鎖

如上圖所示:

第二步,若是MarkWord不是本身的ThreadId,鎖升級,這時候,用CAS來執行切換,新的線程根據MarkWord裏面現有的ThreadId,通知以前線程暫停,以前線程將Markword的內容置爲空。

第三步,兩個線程都把對象的HashCode複製到本身新建的用於存儲鎖的記錄空間,接着開始經過CAS操做,把共享對象的MarKword的內容修改成本身新建的記錄空間的地址的方式競爭MarkWord.

第四步,第三步中成功執行CAS的得到資源,失敗的則進入自旋.

第五步,自旋的線程在自旋過程當中,成功得到資源(即以前獲的資源的線程執行完成並釋放了共享資源),則整個狀態依然處於輕量級鎖的狀態,若是自旋失敗 第六步,進入重量級鎖的狀態,這個時候,自旋的線程進行阻塞,等待以前線程執行完成並喚醒本身.

注意點:JVM加鎖流程

偏向鎖--》輕量級鎖--》重量級鎖

從左往右能夠升級,從右往左不能降級

3、從C++源碼看synchronized

前兩節講了synchronized鎖實現原理,這一節咱們從C++源碼來剖析synchronized。

3.1 同步和互斥

同步:多個線程併發訪問共享資源時,保證同一時刻只有一個(信號量能夠多個)線程使用。

實現同步的方法有不少,常見四種以下:

1)臨界區(CriticalSection,又叫關鍵段):經過對多線程的串行化來訪問公共資源或一段代碼,速度快,適合控制數據訪問。進程內可用。

2)互斥量:互斥量用於線程的互斥。只能爲0/1一個互斥量只能用於一個資源的互斥訪問,可跨進程使用。

3)信號量:信號線用於線程的同步。能夠爲非負整數可實現多個同類資源的多線程互斥和同步。當信號量爲單值信號量是,也能夠完成一個資源的互斥訪問。可跨進程使用。

4)事件:用來通知線程有一些事件已發生,從而啓動後繼任務的開始,可跨進程使用。

synchronized的底層實現就用到了臨界區和互斥鎖(重量級鎖的狀況下)這兩個概念。

3.2 synchronized  C++源碼

重點來了,以前在第一節中的圖1,看過了對象頭Mark Word。如今咱們從C++源碼來剖析具體的數據結構和獲取釋放鎖的過程。

2.2.1 C++中的監視器鎖數據結構

oopDesc--繼承-->markOopDesc--方法monitor()-->ObjectMonitor-->enter、exit 獲取、釋放鎖

1.oopDesc

openjdk\hotspot\src\share\vm\oops\oop.hpp下oopDesc類是JVM對象的頂級基類,故每一個object都包含markOop。以下圖所示:

 1 class oopDesc {
 2   friend class VMStructs;
 3  private:
 4   volatile markOop  _mark;//markOop:Mark Word標記字段
 5   union _metadata {
 6     Klass*      _klass;//對象類型元數據的指針
 7  narrowKlass _compressed_klass;
 8   } _metadata;
 9 
10   // Fast access to barrier set.  Must be initialized.
11   static BarrierSet* _bs;
12 
13  public:
14   markOop  mark() const         { return _mark; }
15   markOop* mark_addr() const    { return (markOop*) &_mark; }
16 
17   void set_mark(volatile markOop m)      { _mark = m;   }
18 
19   void    release_set_mark(markOop m);
20   markOop cas_set_mark(markOop new_mark, markOop old_mark);
21 
22   // Used only to re-initialize the mark word (e.g., of promoted
23   // objects during a GC) -- requires a valid klass pointer
24   void init_mark();
25 
26   Klass* klass() const;
27   Klass* klass_or_null() const volatile;
28   Klass** klass_addr();
29   narrowKlass* compressed_klass_addr();
....省略...
}

2.markOopDesc類

openjdk\hotspot\src\share\vm\oops\markOop.hpp下markOopDesc繼承自oopDesc,並拓展了本身的方法monitor(),以下圖

1 ObjectMonitor* monitor() const {
2     assert(has_monitor(), "check");
3     // Use xor instead of &~ to provide one extra tag-bit check.
4     return (ObjectMonitor*) (value() ^ monitor_value);
5   }

該方法返回一個ObjectMonitor*對象指針。

其中value()這樣定義:

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

可知:將this轉換成一個指針寬度的整數(uintptr_t),而後進行"異或"位操做。

monitor_value是常量
1 enum {   locked_value             = 0,//00偏向鎖 
2          unlocked_value           = 1,//01無鎖
3          monitor_value            = 2,//10監視器鎖,又叫重量級鎖
4          marked_value             = 3,//11GC標記
5          biased_lock_pattern      = 5 //101偏向鎖
6   };
指針低2位00,異或10,結果仍是10.(拿一個模板爲00的數,異或一個二位數=數自己。由於異或:「相同爲0,不一樣爲1」.操做)

3.ObjectMonitor類

在HotSpot虛擬機中,最終採用ObjectMonitor類實現monitor。

openjdk\hotspot\src\share\vm\runtime\objectMonitor.hpp源碼以下:

 1 ObjectMonitor() {  2 _header = NULL;//markOop對象頭  3 _count = 0;  4 _waiters = 0,//等待線程數  5 _recursions = 0;//重入次數  6 _object = NULL;//監視器鎖寄生的對象。鎖不是平白出現的,而是寄託存儲於對象中。  7 _owner = NULL;//指向得到ObjectMonitor對象的線程或基礎鎖  8 _WaitSet = NULL;//處於wait狀態的線程,會被加入到wait set;  9 _WaitSetLock = 0 ; 10 _Responsible = NULL ; 11 _succ = NULL ; 12 _cxq = NULL ; 13 FreeNext = NULL ; 14 _EntryList = NULL ;//處於等待鎖block狀態的線程,會被加入到entry set; 15 _SpinFreq = 0 ; 16 _SpinClock = 0 ; 17 OwnerIsThread = 0 ;// _owner is (Thread *) vs SP/BasicLock 18 _previous_owner_tid = 0;// 監視器前一個擁有者線程的ID 19 }

每一個線程都有兩個ObjectMonitor對象列表,分別爲free和used列表,若是當前free列表爲空,線程將向全局global list請求分配ObjectMonitor。

ObjectMonitor對象中有兩個隊列:_WaitSet 和 _EntryList,用來保存ObjectWaiter對象列表;

2.獲取鎖流程

 synchronized關鍵字修飾的代碼段,在JVM被編譯爲monitorenter、monitorexit指令來獲取和釋放互斥鎖.。

解釋器執行monitorenter時會進入到InterpreterRuntime.cppInterpreterRuntime::monitorenter函數,具體實現以下:

 1 IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
 2 #ifdef ASSERT
 3   thread->last_frame().interpreter_frame_verify_monitor(elem);
 4 #endif
 5   if (PrintBiasedLockingStatistics) {
 6     Atomic::inc(BiasedLocking::slow_path_entry_count_addr());
 7   }
 8   Handle h_obj(thread, elem->obj());
 9   assert(Universe::heap()->is_in_reserved_or_null(h_obj()),
10          "must be NULL or an object");
11   if (UseBiasedLocking) {//標識虛擬機是否開啓偏向鎖功能,默認開啓
12     // Retry fast entry if bias is revoked to avoid unnecessary inflation
13     ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
14   } else {
15     ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
16   }
17   assert(Universe::heap()->is_in_reserved_or_null(elem->obj()),
18          "must be NULL or an object");
19 #ifdef ASSERT
20   thread->last_frame().interpreter_frame_verify_monitor(elem);
21 #endif
22 IRT_END

 

先看一下入參:

一、JavaThread thread指向java中的當前線程;
二、BasicObjectLock基礎對象鎖:包含一個BasicLock和一個指向Object對象的指針oop。

openjdk\hotspot\src\share\vm\runtime\basicLock.hpp中BasicObjectLock類源碼以下:
 1 class BasicObjectLock VALUE_OBJ_CLASS_SPEC {
 2   friend class VMStructs;
 3  private:
 4   BasicLock _lock;                                    // the lock, must be double word aligned
 5   oop       _obj;                                     // object holds the lock;
 6 
 7  public:
 8   // Manipulation
 9   oop      obj() const                                { return _obj;  }
10   void set_obj(oop obj)                               { _obj = obj; }
11   BasicLock* lock()                                   { return &_lock; }
12 
13   // Note: Use frame::interpreter_frame_monitor_size() for the size of BasicObjectLocks
14   //       in interpreter activation frames since it includes machine-specific padding.
15   static int size()                                   { return sizeof(BasicObjectLock)/wordSize; }
16 
17   // GC support
18   void oops_do(OopClosure* f) { f->do_oop(&_obj); }
19 
20   static int obj_offset_in_bytes()                    { return offset_of(BasicObjectLock, _obj);  }
21   static int lock_offset_in_bytes()                   { return offset_of(BasicObjectLock, _lock); }
22 };

三、BasicLock類型_lock對象主要用來保存:指向Object對象的對象頭數據;

basicLock.hpp中BasicLock源碼以下:
 1 class BasicLock VALUE_OBJ_CLASS_SPEC {
 2   friend class VMStructs;
 3  private:
 4   volatile markOop _displaced_header;//markOop是否是很熟悉?1.2節中講解對象頭時就是分析的markOop源碼
 5  public:
 6   markOop      displaced_header() const               { return _displaced_header; }
 7   void         set_displaced_header(markOop header)   { _displaced_header = header; }
 8 
 9   void print_on(outputStream* st) const;
10 
11   // move a basic lock (used during deoptimization
12   void move_to(oop obj, BasicLock* dest);
13 
14   static int displaced_header_offset_in_bytes()       { return offset_of(BasicLock, _displaced_header); }
15 };

偏向鎖的獲取ObjectSynchronizer::fast_enter

在HotSpot中,偏向鎖的入口位於openjdk\hotspot\src\share\vm\runtime\synchronizer.cpp文件的ObjectSynchronizer::fast_enter函數:

 1 void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
 2  if (UseBiasedLocking) {
 3     if (!SafepointSynchronize::is_at_safepoint()) {
 4       BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
 5       if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
 6         return;
 7       }
 8     } else {
 9       assert(!attempt_rebias, "can not rebias toward VM thread");
10       BiasedLocking::revoke_at_safepoint(obj);
11     }
12     assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
13  }
14  //
15  slow_enter (obj, lock, THREAD) ;
16 }輕量級鎖輕量級鎖
偏向鎖的獲取由 BiasedLocking::revoke_and_rebias方法實現,因爲實現比較長,就不貼代碼了,實現邏輯以下:
一、經過markOop mark = obj->mark()獲取對象的markOop數據mark,即對象頭的Mark Word;
二、判斷mark是否爲可偏向狀態,即mark的偏向鎖標誌位爲 1,鎖標誌位爲 01
三、判斷mark中JavaThread的狀態:若是爲空,則進入步驟(4);若是指向當前線程,則執行同步代碼塊;若是指向其它線程,進入步驟(5);
四、經過CAS原子指令設置mark中JavaThread爲當前線程ID,若是執行CAS成功,則執行同步代碼塊,不然進入步驟(5);
五、若是執行CAS失敗,表示當前存在多個線程競爭鎖,當達到全局安全點(safepoint),得到偏向鎖的線程被掛起,撤銷偏向鎖,並升級爲輕量級,升級完成後被阻塞在安全點的線程繼續執行同步代碼塊;
偏向鎖的撤銷

只有當其它線程嘗試競爭偏向鎖時,持有偏向鎖的線程纔會釋放鎖,偏向鎖的撤銷由BiasedLocking::revoke_at_safepoint方法實現:

 

 1 void BiasedLocking::revoke_at_safepoint(Handle h_obj) {
 2   assert(SafepointSynchronize::is_at_safepoint(), "must only be called while at safepoint");//校驗全局安全點  3   oop obj = h_obj();
 4   HeuristicsResult heuristics = update_heuristics(obj, false);
 5   if (heuristics == HR_SINGLE_REVOKE) {
 6     revoke_bias(obj, false, false, NULL);
 7   } else if ((heuristics == HR_BULK_REBIAS) ||
 8              (heuristics == HR_BULK_REVOKE)) {
 9     bulk_revoke_or_rebias_at_safepoint(obj, (heuristics == HR_BULK_REBIAS), false, NULL);
10   }
11   clean_up_cached_monitor_info();
12 }

一、偏向鎖的撤銷動做必須等待全局安全點;
二、暫停擁有偏向鎖的線程,判斷鎖對象是否處於被鎖定狀態;
三、撤銷偏向鎖,恢復到無鎖(標誌位爲 01)或輕量級鎖(標誌位爲 00)的狀態;

偏向鎖在Java 1.6以後是默認啓用的,但在應用程序啓動幾秒鐘以後才激活,可使用-XX:BiasedLockingStartupDelay=0參數關閉延遲,若是肯定應用程序中全部鎖一般狀況下處於競爭狀態,能夠經過XX:-UseBiasedLocking=false參數關閉偏向鎖。

輕量級鎖的獲取
當關閉偏向鎖功能,或多個線程競爭偏向鎖致使偏向鎖升級爲輕量級鎖,會嘗試獲取輕量級鎖,其入口位於 ObjectSynchronizer::slow_enter
 1 void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
 2   markOop mark = obj->mark();
 3   assert(!mark->has_bias_pattern(), "should not see bias pattern here");
 4 
 5   if (mark->is_neutral()) {//是否爲無鎖狀態001
 6     // Anticipate successful CAS -- the ST of the displaced mark must
 7     // be visible <= the ST performed by the CAS.
 8     lock->set_displaced_header(mark);
 9     if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {//CAS成功,釋放棧鎖
10       TEVENT (slow_enter: release stacklock) ;
11       return ;
12     }
13     // Fall through to inflate() ...
14   } else
15   if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
16     assert(lock != mark->locker(), "must not re-lock the same lock");
17     assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock");
18     lock->set_displaced_header(NULL);
19     return;
20   }
21 
22 #if 0
23   // The following optimization isn't particularly useful.
24   if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) {
25     lock->set_displaced_header (NULL) ;
26     return ;
27   }
28 #endif
29 
30   // The object header will never be displaced to this lock,
31   // so it does not matter what the value is, except that it
32   // must be non-zero to avoid looking like a re-entrant lock,
33   // and must not look locked either.
34   lock->set_displaced_header(markOopDesc::unused_mark());
35   ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
36 }

一、markOop mark = obj->mark()方法獲取對象的markOop數據mark;
二、mark->is_neutral()方法判斷mark是否爲無鎖狀態:mark的偏向鎖標誌位爲 0,鎖標誌位爲 01
三、若是mark處於無鎖狀態,則進入步驟(4),不然執行步驟(6);
四、把mark保存到BasicLock對象的_displaced_header字段;
五、經過CAS嘗試將Mark Word更新爲指向BasicLock對象的指針,若是更新成功,表示競爭到鎖,則執行同步代碼,不然執行步驟(6);
六、若是當前mark處於加鎖狀態,且mark中的ptr指針指向當前線程的棧幀,則執行同步代碼,不然說明有多個線程競爭輕量級鎖,輕量級鎖須要膨脹升級爲重量級鎖;

假設線程A和B同時執行到臨界區if (mark->is_neutral())
一、線程AB都把Mark Word複製到各自的_displaced_header字段,該數據保存在線程的棧幀上,是線程私有的;
二、Atomic::cmpxchg_ptr原子操做保證只有一個線程能夠把指向棧幀的指針複製到Mark Word,假設此時線程A執行成功,並返回繼續執行同步代碼塊;
三、線程B執行失敗,退出臨界區,經過ObjectSynchronizer::inflate方法開始膨脹鎖;

輕量級鎖的釋放
輕量級鎖的釋放經過ObjectSynchronizer::slow_exit--->調用 ObjectSynchronizer::fast_exit完成。
 1 void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) {
 2   assert(!object->mark()->has_bias_pattern(), "should not see bias pattern here");
 3   // if displaced header is null, the previous enter is recursive enter, no-op
 4   markOop dhw = lock->displaced_header();
 5   markOop mark ;
 6   if (dhw == NULL) {
 7      // Recursive stack-lock.
 8      // Diagnostics -- Could be: stack-locked, inflating, inflated.
 9      mark = object->mark() ;
10      assert (!mark->is_neutral(), "invariant") ;
11      if (mark->has_locker() && mark != markOopDesc::INFLATING()) {
12         assert(THREAD->is_lock_owned((address)mark->locker()), "invariant") ;
13      }
14      if (mark->has_monitor()) {
15         ObjectMonitor * m = mark->monitor() ;
16         assert(((oop)(m->object()))->mark() == mark, "invariant") ;
17         assert(m->is_entered(THREAD), "invariant") ;
18      }
19      return ;
20   }
21 
22   mark = object->mark() ;
23 
24   // If the object is stack-locked by the current thread, try to
25   // swing the displaced header from the box back to the mark.
26   if (mark == (markOop) lock) {
27      assert (dhw->is_neutral(), "invariant") ;
28      if ((markOop) Atomic::cmpxchg_ptr (dhw, object->mark_addr(), mark) == mark) {//成功的釋放了鎖 29         TEVENT (fast_exit: release stacklock) ;
30         return;
31      }
32   }
33 
34   ObjectSynchronizer::inflate(THREAD, object)->exit (true, THREAD) ;//鎖膨脹升級 35 }
一、確保處於偏向鎖狀態時不會執行這段邏輯;
二、取出在獲取輕量級鎖時保存在BasicLock對象的mark數據dhw;
三、經過CAS嘗試把dhw替換到當前的Mark Word,若是CAS成功,說明成功的釋放了鎖,不然執行步驟(4);
四、若是CAS失敗,說明有其它線程在嘗試獲取該鎖,這時須要將該鎖升級爲重量級鎖,並釋放;

重量級鎖

重量級鎖經過對象內部的監視器(monitor)實現,其中monitor的本質是依賴於底層操做系統的Mutex Lock實現,操做系統實現線程之間的切換須要從用戶態到內核態的切換,切換成本很是高。

鎖膨脹過程

鎖的膨脹過程經過ObjectSynchronizer::inflate函數實現

  1 ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
  2   // Inflate mutates the heap ...
  3   // Relaxing assertion for bug 6320749.
  4   assert (Universe::verify_in_progress() ||
  5           !SafepointSynchronize::is_at_safepoint(), "invariant") ;
  6 
  7   for (;;) {//自旋
  8       const markOop mark = object->mark() ;
  9       assert (!mark->has_bias_pattern(), "invariant") ;
 10 
 11       // The mark can be in one of the following states:
 12       // *  Inflated     - just return
 13       // *  Stack-locked - coerce it to inflated
 14       // *  INFLATING    - busy wait for conversion to complete
 15       // *  Neutral      - aggressively inflate the object.
 16       // *  BIASED       - Illegal.  We should never see this
 17 
 18       // CASE: inflated已膨脹,即重量級鎖
 19       if (mark->has_monitor()) {//判斷當前是否爲重量級鎖
 20           ObjectMonitor * inf = mark->monitor() ;//獲取指向ObjectMonitor的指針
 21           assert (inf->header()->is_neutral(), "invariant");
 22           assert (inf->object() == object, "invariant") ;
 23           assert (ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is invalid");
 24           return inf ;
 25       }
 26 
 27       // CASE: inflation in progress - inflating over a stack-lock.膨脹等待(其餘線程正在從輕量級鎖轉爲膨脹鎖)  28       // Some other thread is converting from stack-locked to inflated.
 29       // Only that thread can complete inflation -- other threads must wait.
 30       // The INFLATING value is transient.
 31       // Currently, we spin/yield/park and poll the markword, waiting for inflation to finish.
 32       // We could always eliminate polling by parking the thread on some auxiliary list.
 33       if (mark == markOopDesc::INFLATING()) {
 34          TEVENT (Inflate: spin while INFLATING) ;
 35          ReadStableMark(object) ;
 36          continue ;
 37       }
 38 
 39       // CASE: stack-locked棧鎖(輕量級鎖)  40       // Could be stack-locked either by this thread or by some other thread.
 41       //
 42       // Note that we allocate the objectmonitor speculatively, _before_ attempting
 43       // to install INFLATING into the mark word.  We originally installed INFLATING,
 44       // allocated the objectmonitor, and then finally STed the address of the
 45       // objectmonitor into the mark.  This was correct, but artificially lengthened
 46       // the interval in which INFLATED appeared in the mark, thus increasing
 47       // the odds of inflation contention.
 48       //
 49       // We now use per-thread private objectmonitor free lists.
 50       // These list are reprovisioned from the global free list outside the
 51       // critical INFLATING...ST interval.  A thread can transfer
 52       // multiple objectmonitors en-mass from the global free list to its local free list.
 53       // This reduces coherency traffic and lock contention on the global free list.
 54       // Using such local free lists, it doesn't matter if the omAlloc() call appears
 55       // before or after the CAS(INFLATING) operation.
 56       // See the comments in omAlloc().
 57 
 58       if (mark->has_locker()) {
 59           ObjectMonitor * m = omAlloc (Self) ;//獲取一個可用的ObjectMonitor 
 60           // Optimistically prepare the objectmonitor - anticipate successful CAS
 61           // We do this before the CAS in order to minimize the length of time
 62           // in which INFLATING appears in the mark.
 63           m->Recycle();
 64           m->_Responsible  = NULL ;
 65           m->OwnerIsThread = 0 ;
 66           m->_recursions   = 0 ;
 67           m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ;   // Consider: maintain by type/class
 68 
 69           markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ;
 70           if (cmp != mark) {//CAS失敗//CAS失敗,說明衝突了,自旋等待//CAS失敗,說明衝突了,自旋等待//CAS失敗,說明衝突了,自旋等待
 71              omRelease (Self, m, true) ;//釋放監視器鎖  72              continue ;       // Interference -- just retry
 73           }
 74 
 75           // We've successfully installed INFLATING (0) into the mark-word.
 76           // This is the only case where 0 will appear in a mark-work.
 77           // Only the singular thread that successfully swings the mark-word
 78           // to 0 can perform (or more precisely, complete) inflation.
 79           //
 80           // Why do we CAS a 0 into the mark-word instead of just CASing the
 81           // mark-word from the stack-locked value directly to the new inflated state?
 82           // Consider what happens when a thread unlocks a stack-locked object.
 83           // It attempts to use CAS to swing the displaced header value from the
 84           // on-stack basiclock back into the object header.  Recall also that the
 85           // header value (hashcode, etc) can reside in (a) the object header, or
 86           // (b) a displaced header associated with the stack-lock, or (c) a displaced
 87           // header in an objectMonitor.  The inflate() routine must copy the header
 88           // value from the basiclock on the owner's stack to the objectMonitor, all
 89           // the while preserving the hashCode stability invariants.  If the owner
 90           // decides to release the lock while the value is 0, the unlock will fail
 91           // and control will eventually pass from slow_exit() to inflate.  The owner
 92           // will then spin, waiting for the 0 value to disappear.   Put another way,
 93           // the 0 causes the owner to stall if the owner happens to try to
 94           // drop the lock (restoring the header from the basiclock to the object)
 95           // while inflation is in-progress.  This protocol avoids races that might
 96           // would otherwise permit hashCode values to change or "flicker" for an object.
 97           // Critically, while object->mark is 0 mark->displaced_mark_helper() is stable.
 98           // 0 serves as a "BUSY" inflate-in-progress indicator.
 99 
100 
101           // fetch the displaced mark from the owner's stack.
102           // The owner can't die or unwind past the lock while our INFLATING
103           // object is in the mark.  Furthermore the owner can't complete
104           // an unlock on the object, either.
105           markOop dmw = mark->displaced_mark_helper() ;
106           assert (dmw->is_neutral(), "invariant") ;
107           //CAS成功,設置ObjectMonitor的_header、_owner和_object等
108           // Setup monitor fields to proper values -- prepare the monitor
109           m->set_header(dmw) ;
110 
111           // Optimization: if the mark->locker stack address is associated
112           // with this thread we could simply set m->_owner = Self and
113           // m->OwnerIsThread = 1. Note that a thread can inflate an object
114           // that it has stack-locked -- as might happen in wait() -- directly
115           // with CAS.  That is, we can avoid the xchg-NULL .... ST idiom.
116           m->set_owner(mark->locker());
117           m->set_object(object);
118           // TODO-FIXME: assert BasicLock->dhw != 0.
119 
120           // Must preserve store ordering. The monitor state must
121           // be stable at the time of publishing the monitor address.
122           guarantee (object->mark() == markOopDesc::INFLATING(), "invariant") ;
123           object->release_set_mark(markOopDesc::encode(m));
124 
125           // Hopefully the performance counters are allocated on distinct cache lines
126           // to avoid false sharing on MP systems ...
127           if (ObjectMonitor::_sync_Inflations != NULL) ObjectMonitor::_sync_Inflations->inc() ;
128           TEVENT(Inflate: overwrite stacklock) ;
129           if (TraceMonitorInflation) {
130             if (object->is_instance()) {
131               ResourceMark rm;
132               tty->print_cr("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
133                 (void *) object, (intptr_t) object->mark(),
134                 object->klass()->external_name());
135             }
136           }
137           return m ;
138       }
139 
140       // CASE: neutral 無鎖
141       // TODO-FIXME: for entry we currently inflate and then try to CAS _owner.
142       // If we know we're inflating for entry it's better to inflate by swinging a
143       // pre-locked objectMonitor pointer into the object header.   A successful
144       // CAS inflates the object *and* confers ownership to the inflating thread.
145       // In the current implementation we use a 2-step mechanism where we CAS()
146       // to inflate and then CAS() again to try to swing _owner from NULL to Self.
147       // An inflateTry() method that we could call from fast_enter() and slow_enter()
148       // would be useful.
149 
150       assert (mark->is_neutral(), "invariant");
151       ObjectMonitor * m = omAlloc (Self) ;
152       // prepare m for installation - set monitor to initial state
153       m->Recycle();
154       m->set_header(mark);
155       m->set_owner(NULL);
156       m->set_object(object);
157       m->OwnerIsThread = 1 ;
158       m->_recursions   = 0 ;
159       m->_Responsible  = NULL ;
160       m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ;       // consider: keep metastats by type/class
161 
162       if (Atomic::cmpxchg_ptr (markOopDesc::encode(m), object->mark_addr(), mark) != mark) {
163           m->set_object (NULL) ;
164           m->set_owner  (NULL) ;
165           m->OwnerIsThread = 0 ;
166           m->Recycle() ;
167           omRelease (Self, m, true) ;
168           m = NULL ;
169           continue ;
170           // interference - the markword changed - just retry.
171           // The state-transitions are one-way, so there's no chance of
172           // live-lock -- "Inflated" is an absorbing state.
173       }
174 
175       // Hopefully the performance counters are allocated on distinct
176       // cache lines to avoid false sharing on MP systems ...
177       if (ObjectMonitor::_sync_Inflations != NULL) ObjectMonitor::_sync_Inflations->inc() ;
178       TEVENT(Inflate: overwrite neutral) ;
179       if (TraceMonitorInflation) {
180         if (object->is_instance()) {
181           ResourceMark rm;
182           tty->print_cr("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
183             (void *) object, (intptr_t) object->mark(),
184             object->klass()->external_name());
185         }
186       }
187       return m ;
188   }
189 }
膨脹過程的實現比較複雜,大概實現過程以下:
一、整個膨脹過程在自旋下完成;
二、 mark->has_monitor()方法判斷當前是否爲重量級鎖(上圖18-25行),即Mark Word的鎖標識位爲 10,若是當前狀態爲重量級鎖,執行步驟(3),不然執行步驟(4);
三、 mark->monitor()方法獲取指向ObjectMonitor的指針,並返回,說明膨脹過程已經完成;
四、若是當前鎖處於 膨脹中(上圖33-37行),說明該鎖正在被其它線程執行膨脹操做,則當前線程就進行自旋等待鎖膨脹完成,這裏須要注意一點,雖然是自旋操做,但不會一直佔用cpu資源,每隔一段時間會經過os::NakedYield方法放棄cpu資源,或經過park方法掛起;若是其餘線程完成鎖的膨脹操做,則退出自旋並返回;
五、若是當前是 輕量級鎖狀態(上圖58-138行),即鎖標識位爲 00,膨脹過程以下:
  1. 經過omAlloc方法,獲取一個可用的ObjectMonitor monitor,並重置monitor數據;
  2. 經過CAS嘗試將Mark Word設置爲markOopDesc:INFLATING,標識當前鎖正在膨脹中,若是CAS失敗,說明同一時刻其它線程已經將Mark Word設置爲markOopDesc:INFLATING,當前線程進行自旋等待膨脹完成;
  3. 若是CAS成功,設置monitor的各個字段:_header、_owner和_object等,並返回;
六、若是是 無鎖(中立,上圖150-186行),重置監視器值;
monitor競爭
當鎖膨脹完成並返回對應的monitor時,並不表示該線程競爭到了鎖,真正的鎖競爭發生在 ObjectMonitor::enter方法中。
  1 void ATTR ObjectMonitor::enter(TRAPS) {
  2   // The following code is ordered to check the most common cases first
  3   // and to reduce RTS->RTO cache line upgrades on SPARC and IA32 processors.
  4   Thread * const Self = THREAD ;
  5   void * cur ;
  6 
  7   cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
  8   if (cur == NULL) {//CAS成功
  9      // Either ASSERT _recursions == 0 or explicitly set _recursions = 0.
 10      assert (_recursions == 0   , "invariant") ;
 11      assert (_owner      == Self, "invariant") ;
 12      // CONSIDER: set or assert OwnerIsThread == 1
 13      return ;
 14   }
 15 
 16   if (cur == Self) {//重入鎖
 17      // TODO-FIXME: check for integer overflow!  BUGID 6557169.
 18      _recursions ++ ;
 19      return ;
 20   }
 21 
 22   if (Self->is_lock_owned ((address)cur)) {
 23     assert (_recursions == 0, "internal state error");
 24     _recursions = 1 ;
 25     // Commute owner from a thread-specific on-stack BasicLockObject address to
 26     // a full-fledged "Thread *".
 27     _owner = Self ;
 28     OwnerIsThread = 1 ;
 29     return ;
 30   }
 31 
 32   // We've encountered genuine contention.
 33   assert (Self->_Stalled == 0, "invariant") ;
 34   Self->_Stalled = intptr_t(this) ;
 35 
 36   // Try one round of spinning *before* enqueueing Self
 37   // and before going through the awkward and expensive state
 38   // transitions.  The following spin is strictly optional ...
 39   // Note that if we acquire the monitor from an initial spin
 40   // we forgo posting JVMTI events and firing DTRACE probes.
 41   if (Knob_SpinEarly && TrySpin (Self) > 0) {
 42      assert (_owner == Self      , "invariant") ;
 43      assert (_recursions == 0    , "invariant") ;
 44      assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ;
 45      Self->_Stalled = 0 ;
 46      return ;
 47   }
 48 
 49   assert (_owner != Self          , "invariant") ;
 50   assert (_succ  != Self          , "invariant") ;
 51   assert (Self->is_Java_thread()  , "invariant") ;
 52   JavaThread * jt = (JavaThread *) Self ;
 53   assert (!SafepointSynchronize::is_at_safepoint(), "invariant") ;
 54   assert (jt->thread_state() != _thread_blocked   , "invariant") ;
 55   assert (this->object() != NULL  , "invariant") ;
 56   assert (_count >= 0, "invariant") ;
 57 
 58   // Prevent deflation at STW-time.  See deflate_idle_monitors() and is_busy().
 59   // Ensure the object-monitor relationship remains stable while there's contention.
 60   Atomic::inc_ptr(&_count);
 61 
 62   EventJavaMonitorEnter event;
 63 
 64   { // Change java thread status to indicate blocked on monitor enter.
 65     JavaThreadBlockedOnMonitorEnterState jtbmes(jt, this);
 66 
 67     DTRACE_MONITOR_PROBE(contended__enter, this, object(), jt);
 68     if (JvmtiExport::should_post_monitor_contended_enter()) {
 69       JvmtiExport::post_monitor_contended_enter(jt, this);
 70     }
 71 
 72     OSThreadContendState osts(Self->osthread());
 73     ThreadBlockInVM tbivm(jt);
 74 
 75     Self->set_current_pending_monitor(this);
 76 
 77     // TODO-FIXME: change the following for(;;) loop to straight-line code.
 78     for (;;) {
 79       jt->set_suspend_equivalent();
 80       // cleared by handle_special_suspend_equivalent_condition()
 81       // or java_suspend_self()
 82 
 83  EnterI (THREAD) ;
 84 
...省略...139 }
一、經過CAS嘗試把monitor的_owner字段設置爲當前線程;
二、若是設置以前的_owner指向當前線程,說明當前線程再次進入monitor,即重入鎖,執行_recursions ++ ,記錄重入的次數;
三、若是以前的_owner指向的地址在當前線程中,這種描述有點拗口,換一種說法:以前_owner指向的BasicLock在當前線程棧上,說明當前線程是第一次進入該monitor,設置_recursions爲1,_owner爲當前線程,該線程成功得到鎖並返回;
四、若是獲取鎖失敗,則等待鎖的釋放;
monitor等待
monitor競爭失敗的線程,經過自旋執行 ObjectMonitor::EnterI方法等待鎖的釋放,EnterI方法的部分邏輯實現以下:
 1 ObjectWaiter node(Self) ;
 2     Self->_ParkEvent->reset() ;
 3     node._prev   = (ObjectWaiter *) 0xBAD ;
 4     node.TState  = ObjectWaiter::TS_CXQ ;
 5 
 6     // Push "Self" onto the front of the _cxq.
 7     // Once on cxq/EntryList, Self stays on-queue until it acquires the lock.
 8     // Note that spinning tends to reduce the rate at which threads
 9     // enqueue and dequeue on EntryList|cxq.
10     ObjectWaiter * nxt ;
11     for (;;) {
12         node._next = nxt = _cxq ;
13         if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;
14 
15         // Interference - the CAS failed because _cxq changed.  Just retry.
16         // As an optional optimization we retry the lock.
17         if (TryLock (Self) > 0) {
18             assert (_succ != Self         , "invariant") ;
19             assert (_owner == Self        , "invariant") ;
20             assert (_Responsible != Self  , "invariant") ;
21             return ;
22         }
23     }
一、當前線程被封裝成ObjectWaiter對象node,狀態設置成ObjectWaiter::TS_CXQ;
二、在for循環中,經過CAS把node節點push到_cxq列表中,同一時刻可能有多個線程把本身的node節點push到_cxq列表中;
三、node節點push到_cxq列表以後,經過自旋嘗試獲取鎖,若是仍是沒有獲取到鎖,則經過park將當前線程掛起,等待被喚醒,實現以下:
 1 for (;;) {
 2 
 3         if (TryLock (Self) > 0) break ;
 4         assert (_owner != Self, "invariant") ;
 5 
 6         if ((SyncFlags & 2) && _Responsible == NULL) {
 7            Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ;
 8         }
 9 
10         // park self
11         if (_Responsible == Self || (SyncFlags & 1)) {
12             TEVENT (Inflated enter - park TIMED) ;
13             Self->_ParkEvent->park ((jlong) RecheckInterval) ;
14             // Increase the RecheckInterval, but clamp the value.
15             RecheckInterval *= 8 ;
16             if (RecheckInterval > 1000) RecheckInterval = 1000 ;
17         } else {
18             TEVENT (Inflated enter - park UNTIMED) ;
19             Self->_ParkEvent->park() ;//當前線程掛起
20         }
21 
22         if (TryLock(Self) > 0) break ;
23 
24         // The lock is still contested.
25         // Keep a tally of the # of futile wakeups.
26         // Note that the counter is not protected by a lock or updated by atomics.
27         // That is by design - we trade "lossy" counters which are exposed to
28         // races during updates for a lower probe effect.
29         TEVENT (Inflated enter - Futile wakeup) ;
30         if (ObjectMonitor::_sync_FutileWakeups != NULL) {
31            ObjectMonitor::_sync_FutileWakeups->inc() ;
32         }
33         ++ nWakeups ;
34 
35         // Assuming this is not a spurious wakeup we'll normally find _succ == Self.
36         // We can defer clearing _succ until after the spin completes
37         // TrySpin() must tolerate being called with _succ == Self.
38         // Try yet another round of adaptive spinning.
39         if ((Knob_SpinAfterFutile & 1) && TrySpin (Self) > 0) break ;
40 
41         // We can find that we were unpark()ed and redesignated _succ while
42         // we were spinning.  That's harmless.  If we iterate and call park(),
43         // park() will consume the event and return immediately and we'll
44         // just spin again.  This pattern can repeat, leaving _succ to simply
45         // spin on a CPU.  Enable Knob_ResetEvent to clear pending unparks().
46         // Alternately, we can sample fired() here, and if set, forgo spinning
47         // in the next iteration.
48 
49         if ((Knob_ResetEvent & 1) && Self->_ParkEvent->fired()) {
50            Self->_ParkEvent->reset() ;
51            OrderAccess::fence() ;
52         }
53         if (_succ == Self) _succ = NULL ;
54 
55         // Invariant: after clearing _succ a thread *must* retry _owner before parking.
56         OrderAccess::fence() ;
57     }

四、當該線程被喚醒時,會從掛起的點繼續執行,經過ObjectMonitor::TryLock嘗試獲取鎖,TryLock方法實現以下:

 1 int ObjectMonitor::TryLock (Thread * Self) {
 2    for (;;) {
 3       void * own = _owner ;
 4       if (own != NULL) return 0 ;
 5       if (Atomic::cmpxchg_ptr (Self, &_owner, NULL) == NULL) {//CAS成功,獲取鎖  6          // Either guarantee _recursions == 0 or set _recursions = 0.
 7          assert (_recursions == 0, "invariant") ;
 8          assert (_owner == Self, "invariant") ;
 9          // CONSIDER: set or assert that OwnerIsThread == 1
10          return 1 ;
11       }
12       // The lock had been free momentarily, but we lost the race to the lock.
13       // Interference -- the CAS failed.
14       // We can either return -1 or retry.
15       // Retry doesn't make as much sense because the lock was just acquired.
16       if (true) return -1 ;
17    }
18 }

其本質就是經過CAS設置monitor的_owner字段爲當前線程,若是CAS成功,則表示該線程獲取了鎖,跳出自旋操做,執行同步代碼,不然繼續被掛起;

monitor釋放

當某個持有鎖的線程執行完同步代碼塊時,會進行鎖的釋放,給其它線程機會執行同步代碼,在HotSpot中,經過退出monitor的方式實現鎖的釋放,並通知被阻塞的線程,具體實現位於ObjectMonitor::exit方法中。

 1 void ATTR ObjectMonitor::exit(bool not_suspended, TRAPS) {
 2    Thread * Self = THREAD ;
 3    if (THREAD != _owner) {
 4      if (THREAD->is_lock_owned((address) _owner)) {
 5        // Transmute _owner from a BasicLock pointer to a Thread address.
 6        // We don't need to hold _mutex for this transition.
 7        // Non-null to Non-null is safe as long as all readers can
 8        // tolerate either flavor.
 9        assert (_recursions == 0, "invariant") ;
10        _owner = THREAD ;
11        _recursions = 0 ;
12        OwnerIsThread = 1 ;
13      } else {
14        // NOTE: we need to handle unbalanced monitor enter/exit
15        // in native code by throwing an exception.
16        // TODO: Throw an IllegalMonitorStateException ?
17        TEVENT (Exit - Throw IMSX) ;
18        assert(false, "Non-balanced monitor enter/exit!");
19        if (false) {
20           THROW(vmSymbols::java_lang_IllegalMonitorStateException());
21        }
22        return;
23      }
24    }
25 
26    if (_recursions != 0) {
27      _recursions--;        // this is simple recursive enter
28      TEVENT (Inflated exit - recursive) ;
29      return ;
30    }
...省略...

一、若是是重量級鎖的釋放,monitor中的_owner指向當前線程,即THREAD == _owner;
二、根據不一樣的策略(由QMode指定),從cxq或EntryList中獲取頭節點,經過 ObjectMonitor::ExitEpilog方法 喚醒該節點封裝的線程,喚醒操做最終由unpark完成,實現以下:
 1 void ObjectMonitor::ExitEpilog (Thread * Self, ObjectWaiter * Wakee) {
 2    assert (_owner == Self, "invariant") ;
 3 
 4    // Exit protocol:
 5    // 1. ST _succ = wakee
 6    // 2. membar #loadstore|#storestore;
 7    // 2. ST _owner = NULL
 8    // 3. unpark(wakee)
 9 
10    _succ = Knob_SuccEnabled ? Wakee->_thread : NULL ;
11    ParkEvent * Trigger = Wakee->_event ;
12 
13    // Hygiene -- once we've set _owner = NULL we can't safely dereference Wakee again.
14    // The thread associated with Wakee may have grabbed the lock and "Wakee" may be
15    // out-of-scope (non-extant).
16    Wakee  = NULL ;
17 
18    // Drop the lock
19    OrderAccess::release_store_ptr (&_owner, NULL) ;
20    OrderAccess::fence() ;                               // ST _owner vs LD in unpark()
21 
22    if (SafepointSynchronize::do_call_back()) {
23       TEVENT (unpark before SAFEPOINT) ;
24    }
25 
26    DTRACE_MONITOR_PROBE(contended__exit, this, object(), Self);
27    Trigger->unpark() ;
28 
29    // Maintain stats and report events to JVMTI
30    if (ObjectMonitor::_sync_Parks != NULL) {
31       ObjectMonitor::_sync_Parks->inc() ;
32    }
33 }

 

三、被喚醒的線程,繼續執行monitor的競爭;

四.總結

本文重點介紹了Synchronized原理以及JVM對Synchronized的優化。簡單來講解決三種場景:

1)只有一個線程進入臨界區,偏向鎖

2)多個線程交替進入臨界區,輕量級鎖

3)多線程同時進入臨界區,重量級鎖

 

 

========================================================

 

參考:

《深刻理解 Java 虛擬機》

JVM源碼分析之synchronized實現

相關文章
相關標籤/搜索