国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
jdk源碼剖析二: 對(duì)象內(nèi)存布局、synchronized終極原理

目錄

1.啟蒙知識(shí)預(yù)熱:CAS原理+JVM對(duì)象頭內(nèi)存存儲(chǔ)結(jié)構(gòu)

2.JVM中synchronized鎖實(shí)現(xiàn)原理(優(yōu)化)

3.從C++源碼看synchronized

4.總結(jié)

 

很多人一提到鎖,自然第一個(gè)想到了synchronized,但一直不懂源碼實(shí)現(xiàn),現(xiàn)特地追蹤到C++層來(lái)剝開(kāi)synchronized的面紗。

網(wǎng)上的很多描述大都不全,讓人看了不夠爽,看完本章,你將徹底了解synchronized的核心原理。


 

一、啟蒙知識(shí)預(yù)熱

開(kāi)啟本文之前先介紹2個(gè)概念

1.1.cas操作

為了提高性能,JVM很多操作都依賴CAS實(shí)現(xiàn),一種樂(lè)觀鎖的實(shí)現(xiàn)。本文鎖優(yōu)化中大量用到了CAS,故有必要先分析一下CAS的實(shí)現(xiàn)。

CAS:Compare and Swap。

JNI來(lái)完成CPU指令的操作:

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

CAS有3個(gè)操作數(shù),內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的新值B。如果A=V,那么把B賦值給V,返回V;如果A!=V,直接返回V。

打開(kāi)源碼:openjdk\hotspot\src\oscpu\windowsx86\vm\ atomicwindowsx86.inline.hpp,如下圖:0

os::is_MP()  這個(gè)是runtime/os.hpp,實(shí)際就是返回是否多處理器,源碼如下:

 

如上面源代碼所示(看第一個(gè)int參數(shù)即可),LOCK_IF_MP:會(huì)根據(jù)當(dāng)前處理器的類型來(lái)決定是否為cmpxchg指令添加lock前綴。如果程序是在多處理器上運(yùn)行,就為cmpxchg指令加上lock前綴(lock cmpxchg)。反之,如果程序是在單處理器上運(yùn)行,就省略lock前綴(單處理器自身會(huì)維護(hù)單處理器內(nèi)的順序一致性,不需要lock前綴提供的內(nèi)存屏障效果)。

 

1.2.對(duì)象頭

HotSpot虛擬機(jī)中,對(duì)象在內(nèi)存中存儲(chǔ)的布局可以分為三塊區(qū)域:對(duì)象頭(Header)、實(shí)例數(shù)據(jù)(Instance Data)和對(duì)齊填充(Padding)。

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

第一部分"Mark Word":用于存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù), 如哈希碼(HashCode)、GC分代年齡、鎖狀態(tài)標(biāo)志、線程持有的鎖、偏向線程ID、偏向時(shí)間戳等等.

第二部分"Klass Pointer":對(duì)象指向它的類的元數(shù)據(jù)的指針,虛擬機(jī)通過(guò)這個(gè)指針來(lái)確定這個(gè)對(duì)象是哪個(gè)類的實(shí)例。(數(shù)組,對(duì)象頭中還必須有一塊用于記錄數(shù)組長(zhǎng)度的數(shù)據(jù),因?yàn)樘摂M機(jī)可以通過(guò)普通Java對(duì)象的元數(shù)據(jù)信息確定Java對(duì)象的大小,但是從數(shù)組的元數(shù)據(jù)中無(wú)法確定數(shù)組的大小。 )

32位的HotSpot虛擬機(jī)對(duì)象頭存儲(chǔ)結(jié)構(gòu):(下圖摘自網(wǎng)絡(luò))

                                                              圖1 32位的HotSpot虛擬機(jī)對(duì)象頭Mark Word組成

為了證實(shí)上圖的正確性,這里我們看openJDK--》hotspot源碼markOop.hpp,虛擬機(jī)對(duì)象頭存儲(chǔ)結(jié)構(gòu):

                                             圖2 HotSpot源碼markOop.hpp中注釋

單詞解釋:

hash: 保存對(duì)象的哈希碼
age: 保存對(duì)象的分代年齡
biased_lock: 偏向鎖標(biāo)識(shí)位
lock: 鎖狀態(tài)標(biāo)識(shí)位
JavaThread*: 保存持有偏向鎖的線程ID
epoch: 保存偏向時(shí)間戳

上圖中有源碼中對(duì)鎖標(biāo)志位這樣枚舉

1 enum {   locked_value             = 0,//00 輕量級(jí)鎖2          unlocked_value           = 1,//01 無(wú)鎖3          monitor_value            = 2,//10 監(jiān)視器鎖,也叫膨脹鎖,也叫重量級(jí)鎖4          marked_value             = 3,//11 GC標(biāo)記5          biased_lock_pattern      = 5 //101 偏向鎖6   };

下面是源碼注釋:

                                        圖3 HotSpot源碼markOop.hpp中鎖標(biāo)志位注釋

看圖3,不管是32/64位JVM,都是1bit偏向鎖+2bit鎖標(biāo)志位。上面紅框是偏向鎖(第一行是指向線程的顯示偏向鎖,第二行是匿名偏向鎖)對(duì)應(yīng)枚舉biased_lock_pattern,下面紅框是輕量級(jí)鎖、無(wú)鎖、監(jiān)視器鎖、GC標(biāo)記,分別對(duì)應(yīng)上面的前4種枚舉。我們甚至能看見(jiàn)鎖標(biāo)志11時(shí),是GC的markSweep(標(biāo)記清除算法)使用的。(這里就不再拓展了)

對(duì)象頭中的Mark Word,synchronized源碼實(shí)現(xiàn)就用了Mark Word來(lái)標(biāo)識(shí)對(duì)象加鎖狀態(tài)。

二、JVM中synchronized鎖實(shí)現(xiàn)原理(優(yōu)化)

大家都知道java中鎖synchronized性能較差,線程會(huì)阻塞。本節(jié)將以圖文形式來(lái)描述JVM的synchronized鎖優(yōu)化。

在jdk1.6中對(duì)鎖的實(shí)現(xiàn)引入了大量的優(yōu)化來(lái)減少鎖操作的開(kāi)銷:

鎖粗化(Lock Coarsening):將多個(gè)連續(xù)的鎖擴(kuò)展成一個(gè)范圍更大的鎖,用以減少頻繁互斥同步導(dǎo)致的性能損耗。鎖消除(Lock Elimination):JVM及時(shí)編譯器在運(yùn)行時(shí),通過(guò)逃逸分析,如果判斷一段代碼中,堆上的所有數(shù)據(jù)不會(huì)逃逸出去從來(lái)不會(huì)被其他線程訪問(wèn)到,就可以去除這些鎖。輕量級(jí)鎖(Lightweight Locking):JDK1.6引入。在沒(méi)有多線程競(jìng)爭(zhēng)的情況下避免重量級(jí)互斥鎖,只需要依靠一條CAS原子指令就可以完成鎖的獲取及釋放。偏向鎖(Biased Locking):JDK1.6引入。目的是消除數(shù)據(jù)在無(wú)競(jìng)爭(zhēng)情況下的同步原語(yǔ)。使用CAS記錄獲取它的線程。下一次同一個(gè)線程進(jìn)入則偏向該線程,無(wú)需任何同步操作。適應(yīng)性自旋(Adaptive Spinning):為了避免線程頻繁掛起、恢復(fù)的狀態(tài)切換消耗。產(chǎn)生了忙循環(huán)(循環(huán)時(shí)間固定),即自旋。JDK1.6引入了自適應(yīng)自旋。自旋時(shí)間根據(jù)之前鎖自旋時(shí)間和線程狀態(tài),動(dòng)態(tài)變化,用以期望能減少阻塞的時(shí)間。

 鎖升級(jí):偏向鎖--》輕量級(jí)鎖--》重量級(jí)鎖

2.1.偏向鎖

  按照之前的HotSpot設(shè)計(jì),每次加鎖/解鎖都會(huì)涉及到一些CAS操作(比如對(duì)等待隊(duì)列的CAS操作),CAS操作會(huì)延遲本地調(diào)用,因此偏向鎖的想法是一旦線程第一次獲得了監(jiān)視對(duì)象,之后讓監(jiān)視對(duì)象“偏向”這個(gè)線程,之后的多次調(diào)用則可以避免CAS操作。
  簡(jiǎn)單的講,就是在鎖對(duì)象的對(duì)象頭(開(kāi)篇講的對(duì)象頭數(shù)據(jù)存儲(chǔ)結(jié)構(gòu))中有個(gè)ThreaddId字段,這個(gè)字段如果是空的,第一次獲取鎖的時(shí)候,就將自身的ThreadId寫入到鎖的ThreadId字段內(nèi),將鎖頭內(nèi)的是否偏向鎖的狀態(tài)位置1.這樣下次獲取鎖的時(shí)候,直接檢查ThreadId是否和自身線程Id一致,如果一致,則認(rèn)為當(dāng)前線程已經(jīng)獲取了鎖,因此不需再次獲取鎖,略過(guò)了輕量級(jí)鎖和重量級(jí)鎖的加鎖階段。提高了效率。
注意:當(dāng)鎖有競(jìng)爭(zhēng)關(guān)系的時(shí)候,需要解除偏向鎖,進(jìn)入輕量級(jí)鎖。

每一個(gè)線程在準(zhǔn)備獲取共享資源時(shí):

第一步,檢查MarkWord里面是不是放的自己的ThreadId ,如果是,表示當(dāng)前線程是處于 “偏向鎖”.跳過(guò)輕量級(jí)鎖直接執(zhí)行同步體。

獲得偏向鎖如下圖:

 

2.2.輕量級(jí)鎖和重量級(jí)鎖

如上圖所示:

第二步,如果MarkWord不是自己的ThreadId,鎖升級(jí),這時(shí)候,用CAS來(lái)執(zhí)行切換,新的線程根據(jù)MarkWord里面現(xiàn)有的ThreadId,通知之前線程暫停,之前線程將Markword的內(nèi)容置為空。

第三步,兩個(gè)線程都把對(duì)象的HashCode復(fù)制到自己新建的用于存儲(chǔ)鎖的記錄空間,接著開(kāi)始通過(guò)CAS操作,把共享對(duì)象的MarKword的內(nèi)容修改為自己新建的記錄空間的地址的方式競(jìng)爭(zhēng)MarkWord.

第四步,第三步中成功執(zhí)行CAS的獲得資源,失敗的則進(jìn)入自旋.

第五步,自旋的線程在自旋過(guò)程中,成功獲得資源(即之前獲的資源的線程執(zhí)行完成并釋放了共享資源),則整個(gè)狀態(tài)依然處于輕量級(jí)鎖的狀態(tài),如果自旋失敗 第六步,進(jìn)入重量級(jí)鎖的狀態(tài),這個(gè)時(shí)候,自旋的線程進(jìn)行阻塞,等待之前線程執(zhí)行完成并喚醒自己.

注意點(diǎn):JVM加鎖流程

偏向鎖--》輕量級(jí)鎖--》重量級(jí)鎖

從左往右可以升級(jí),從右往左不能降級(jí)

三、從C++源碼看synchronized

前兩節(jié)講了synchronized鎖實(shí)現(xiàn)原理,這一節(jié)我們從C++源碼來(lái)剖析synchronized。

3.1 同步和互斥

同步:多個(gè)線程并發(fā)訪問(wèn)共享資源時(shí),保證同一時(shí)刻只有一個(gè)(信號(hào)量可以多個(gè))線程使用。

實(shí)現(xiàn)同步的方法有很多,常見(jiàn)四種如下:

1)臨界區(qū)(CriticalSection,又叫關(guān)鍵段):通過(guò)對(duì)多線程的串行化來(lái)訪問(wèn)公共資源或一段代碼,速度快,適合控制數(shù)據(jù)訪問(wèn)。進(jìn)程內(nèi)可用。

2)互斥量:互斥量用于線程的互斥。只能為0/1。一個(gè)互斥量只能用于一個(gè)資源的互斥訪問(wèn),可跨進(jìn)程使用。

3)信號(hào)量:信號(hào)線用于線程的同步。可以為非負(fù)整數(shù),可實(shí)現(xiàn)多個(gè)同類資源的多線程互斥和同步。當(dāng)信號(hào)量為單值信號(hào)量時(shí),也可以完成一個(gè)資源的互斥訪問(wèn)??煽邕M(jìn)程使用。

4)事件:用來(lái)通知線程有一些事件已發(fā)生,從而啟動(dòng)后繼任務(wù)的開(kāi)始,可跨進(jìn)程使用。

synchronized的底層實(shí)現(xiàn)就用到了臨界區(qū)和互斥鎖(重量級(jí)鎖的情況下)這兩個(gè)概念。

3.2 synchronized  C++源碼

重點(diǎn)來(lái)了,之前在第一節(jié)中的圖1,看過(guò)了對(duì)象頭Mark Word。現(xiàn)在我們從C++源碼來(lái)剖析具體的數(shù)據(jù)結(jié)構(gòu)和獲取釋放鎖的過(guò)程。

2.2.1 C++中的監(jiān)視器鎖數(shù)據(jù)結(jié)構(gòu)

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

1.oopDesc

openjdk\hotspot\src\share\vm\oops\oop.hpp下oopDesc類是JVM對(duì)象的頂級(jí)基類,故每個(gè)object都包含markOop。如下圖所示:

 

 1 class oopDesc { 2   friend class VMStructs; 3  private: 4   volatile markOop  _mark;//markOop:Mark Word標(biāo)記字段 5   union _metadata { 6     Klass*      _klass;//對(duì)象類型元數(shù)據(jù)的指針 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 promoted23   // objects during a GC) -- requires a valid klass pointer24   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類

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   }

該方法返回一個(gè)ObjectMonitor*對(duì)象指針。

其中value()這樣定義:

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

monitor_value是常量
1 enum {   locked_value             = 0,//00偏向鎖 2          unlocked_value           = 1,//01無(wú)鎖3          monitor_value            = 2,//10監(jiān)視器鎖,又叫重量級(jí)鎖4          marked_value             = 3,//11GC標(biāo)記5          biased_lock_pattern      = 5 //101偏向鎖6   };

3.ObjectMonitor類

在HotSpot虛擬機(jī)中,最終采用ObjectMonitor類實(shí)現(xiàn)monitor。

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

 1 ObjectMonitor() { 2     _header       = NULL;//markOop對(duì)象頭 3     _count        = 0; 4     _waiters      = 0,//等待線程數(shù) 5     _recursions   = 0;//重入次數(shù) 6     _object       = NULL; 7     _owner        = NULL;//指向獲得ObjectMonitor對(duì)象的線程或基礎(chǔ)鎖 8     _WaitSet      = NULL;//處于wait狀態(tài)的線程,會(huì)被加入到wait set; 9     _WaitSetLock  = 0 ;10     _Responsible  = NULL ;11     _succ         = NULL ;12     _cxq          = NULL ;13     FreeNext      = NULL ;14     _EntryList    = NULL ;//處于等待鎖block狀態(tài)的線程,會(huì)被加入到entry set;15     _SpinFreq     = 0 ;16     _SpinClock    = 0 ;17     OwnerIsThread = 0 ;// _owner is (Thread *) vs SP/BasicLock18     _previous_owner_tid = 0;// 監(jiān)視器前一個(gè)擁有者線程的ID19   }

每個(gè)線程都有兩個(gè)ObjectMonitor對(duì)象列表,分別為free和used列表,如果當(dāng)前free列表為空,線程將向全局global list請(qǐng)求分配ObjectMonitor。

ObjectMonitor對(duì)象中有兩個(gè)隊(duì)列:_WaitSet 和 _EntryList,用來(lái)保存ObjectWaiter對(duì)象列表;

2.獲取鎖流程

 synchronized關(guān)鍵字修飾的代碼段,在JVM被編譯為monitorenter、monitorexit指令來(lái)獲取和釋放互斥鎖.。

解釋器執(zhí)行monitorenter時(shí)會(huì)進(jìn)入到InterpreterRuntime.cppInterpreterRuntime::monitorenter函數(shù),具體實(shí)現(xiàn)如下:

 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) {//標(biāo)識(shí)虛擬機(jī)是否開(kāi)啟偏向鎖功能,默認(rèn)開(kāi)啟12     // Retry fast entry if bias is revoked to avoid unnecessary inflation13     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 ASSERT20   thread->last_frame().interpreter_frame_verify_monitor(elem);21 #endif22 IRT_END

 

先看一下入?yún)ⅲ?/p>

1、JavaThread thread指向java中的當(dāng)前線程;
2、BasicObjectLock基礎(chǔ)對(duì)象鎖:包含一個(gè)BasicLock和一個(gè)指向Object對(duì)象的指針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 BasicObjectLocks14   //       in interpreter activation frames since it includes machine-specific padding.15   static int size()                                   { return sizeof(BasicObjectLock)/wordSize; }16 17   // GC support18   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 };

3、BasicLock類型_lock對(duì)象主要用來(lái)保存:指向Object對(duì)象的對(duì)象頭數(shù)據(jù);

basicLock.hpp中BasicLock源碼如下:
 1 class BasicLock VALUE_OBJ_CLASS_SPEC { 2   friend class VMStructs; 3  private: 4   volatile markOop _displaced_header;//markOop是不是很熟悉?1.2節(jié)中講解對(duì)象頭時(shí)就是分析的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 deoptimization12   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函數(shù):

 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  //輕量級(jí)鎖15  slow_enter (obj, lock, THREAD) ;16 }
偏向鎖的獲取由BiasedLocking::revoke_and_rebias方法實(shí)現(xiàn),由于實(shí)現(xiàn)比較長(zhǎng),就不貼代碼了,實(shí)現(xiàn)邏輯如下:
1、通過(guò)markOop mark = obj->mark()獲取對(duì)象的markOop數(shù)據(jù)mark,即對(duì)象頭的Mark Word;
2、判斷mark是否為可偏向狀態(tài),即mark的偏向鎖標(biāo)志位為 1,鎖標(biāo)志位為 01;
3、判斷mark中JavaThread的狀態(tài):如果為空,則進(jìn)入步驟(4);如果指向當(dāng)前線程,則執(zhí)行同步代碼塊;如果指向其它線程,進(jìn)入步驟(5);
4、通過(guò)CAS原子指令設(shè)置mark中JavaThread為當(dāng)前線程ID,如果執(zhí)行CAS成功,則執(zhí)行同步代碼塊,否則進(jìn)入步驟(5);
5、如果執(zhí)行CAS失敗,表示當(dāng)前存在多個(gè)線程競(jìng)爭(zhēng)鎖,當(dāng)達(dá)到全局安全點(diǎn)(safepoint),獲得偏向鎖的線程被掛起,撤銷偏向鎖,并升級(jí)為輕量級(jí),升級(jí)完成后被阻塞在安全點(diǎn)的線程繼續(xù)執(zhí)行同步代碼塊;
偏向鎖的撤銷

只有當(dāng)其它線程嘗試競(jìng)爭(zhēng)偏向鎖時(shí),持有偏向鎖的線程才會(huì)釋放鎖,偏向鎖的撤銷由BiasedLocking::revoke_at_safepoint方法實(shí)現(xiàn):

 

 1 void BiasedLocking::revoke_at_safepoint(Handle h_obj) { 2   assert(SafepointSynchronize::is_at_safepoint(), "must only be called while at safepoint");//校驗(yàn)全局安全點(diǎn) 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 }

1、偏向鎖的撤銷動(dòng)作必須等待全局安全點(diǎn);
2、暫停擁有偏向鎖的線程,判斷鎖對(duì)象是否處于被鎖定狀態(tài);
3、撤銷偏向鎖,恢復(fù)到無(wú)鎖(標(biāo)志位為 01)或輕量級(jí)鎖(標(biāo)志位為 00)的狀態(tài);

偏向鎖在Java 1.6之后是默認(rèn)啟用的,但在應(yīng)用程序啟動(dòng)幾秒鐘之后才激活,可以使用-XX:BiasedLockingStartupDelay=0參數(shù)關(guān)閉延遲,如果確定應(yīng)用程序中所有鎖通常情況下處于競(jìng)爭(zhēng)狀態(tài),可以通過(guò)XX:-UseBiasedLocking=false參數(shù)關(guān)閉偏向鎖。

輕量級(jí)鎖的獲取
當(dāng)關(guān)閉偏向鎖功能,或多個(gè)線程競(jìng)爭(zhēng)偏向鎖導(dǎo)致偏向鎖升級(jí)為輕量級(jí)鎖,會(huì)嘗試獲取輕量級(jí)鎖,其入口位于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()) {//是否為無(wú)鎖狀態(tài)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   } else15   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 023   // 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 #endif29 30   // The object header will never be displaced to this lock,31   // so it does not matter what the value is, except that it32   // 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 }

1、markOop mark = obj->mark()方法獲取對(duì)象的markOop數(shù)據(jù)mark;
2、mark->is_neutral()方法判斷mark是否為無(wú)鎖狀態(tài):mark的偏向鎖標(biāo)志位為 0,鎖標(biāo)志位為 01;
3、如果mark處于無(wú)鎖狀態(tài),則進(jìn)入步驟(4),否則執(zhí)行步驟(6);
4、把mark保存到BasicLock對(duì)象的_displaced_header字段;
5、通過(guò)CAS嘗試將Mark Word更新為指向BasicLock對(duì)象的指針,如果更新成功,表示競(jìng)爭(zhēng)到鎖,則執(zhí)行同步代碼,否則執(zhí)行步驟(6);
6、如果當(dāng)前mark處于加鎖狀態(tài),且mark中的ptr指針指向當(dāng)前線程的棧幀,則執(zhí)行同步代碼,否則說(shuō)明有多個(gè)線程競(jìng)爭(zhēng)輕量級(jí)鎖,輕量級(jí)鎖需要膨脹升級(jí)為重量級(jí)鎖;

假設(shè)線程A和B同時(shí)執(zhí)行到臨界區(qū)if (mark->is_neutral())
1、線程AB都把Mark Word復(fù)制到各自的_displaced_header字段,該數(shù)據(jù)保存在線程的棧幀上,是線程私有的;
2、Atomic::cmpxchg_ptr原子操作保證只有一個(gè)線程可以把指向棧幀的指針復(fù)制到Mark Word,假設(shè)此時(shí)線程A執(zhí)行成功,并返回繼續(xù)執(zhí)行同步代碼塊;
3、線程B執(zhí)行失敗,退出臨界區(qū),通過(guò)ObjectSynchronizer::inflate方法開(kāi)始膨脹鎖;

輕量級(jí)鎖的釋放
輕量級(jí)鎖的釋放通過(guò)ObjectSynchronizer::slow_exit--->調(diào)用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 to25   // 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) ;//鎖膨脹升級(jí)35 }
1、確保處于偏向鎖狀態(tài)時(shí)不會(huì)執(zhí)行這段邏輯;
2、取出在獲取輕量級(jí)鎖時(shí)保存在BasicLock對(duì)象的mark數(shù)據(jù)dhw;
3、通過(guò)CAS嘗試把dhw替換到當(dāng)前的Mark Word,如果CAS成功,說(shuō)明成功的釋放了鎖,否則執(zhí)行步驟(4);
4、如果CAS失敗,說(shuō)明有其它線程在嘗試獲取該鎖,這時(shí)需要將該鎖升級(jí)為重量級(jí)鎖,并釋放;

重量級(jí)鎖

重量級(jí)鎖通過(guò)對(duì)象內(nèi)部的監(jiān)視器(monitor)實(shí)現(xiàn),其中monitor的本質(zhì)是依賴于底層操作系統(tǒng)的Mutex Lock實(shí)現(xiàn),操作系統(tǒng)實(shí)現(xiàn)線程之間的切換需要從用戶態(tài)到內(nèi)核態(tài)的切換,切換成本非常高。

鎖膨脹過(guò)程

鎖的膨脹過(guò)程通過(guò)ObjectSynchronizer::inflate函數(shù)實(shí)現(xiàn)

  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已膨脹,即重量級(jí)鎖 19       if (mark->has_monitor()) {//判斷當(dāng)前是否為重量級(jí)鎖 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.膨脹等待(其他線程正在從輕量級(jí)鎖轉(zhuǎn)為膨脹鎖) 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棧鎖(輕量級(jí)鎖)  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) ;//獲取一個(gè)可用的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失敗,說(shuō)明沖突了,自旋等待//CAS失敗,說(shuō)明沖突了,自旋等待//CAS失敗,說(shuō)明沖突了,自旋等待 71              omRelease (Self, m, true) ;//釋放監(jiān)視器鎖 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 INFLATING103           // object is in the mark.  Furthermore the owner can't complete104           // an unlock on the object, either.105           markOop dmw = mark->displaced_mark_helper() ;106           assert (dmw->is_neutral(), "invariant") ;107           //CAS成功,設(shè)置ObjectMonitor的_header、_owner和_object等108           // Setup monitor fields to proper values -- prepare the monitor109           m->set_header(dmw) ;110 111           // Optimization: if the mark->locker stack address is associated112           // with this thread we could simply set m->_owner = Self and113           // m->OwnerIsThread = 1. Note that a thread can inflate an object114           // that it has stack-locked -- as might happen in wait() -- directly115           // 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 must121           // 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 lines126           // 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 無(wú)鎖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 a143       // pre-locked objectMonitor pointer into the object header.   A successful144       // 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 state153       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/class161 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 of172           // live-lock -- "Inflated" is an absorbing state.173       }174 175       // Hopefully the performance counters are allocated on distinct176       // 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 }
膨脹過(guò)程的實(shí)現(xiàn)比較復(fù)雜,大概實(shí)現(xiàn)過(guò)程如下:
1、整個(gè)膨脹過(guò)程在自旋下完成;
2、mark->has_monitor()方法判斷當(dāng)前是否為重量級(jí)鎖(上圖18-25行),即Mark Word的鎖標(biāo)識(shí)位為 10,如果當(dāng)前狀態(tài)為重量級(jí)鎖,執(zhí)行步驟(3),否則執(zhí)行步驟(4);
3、mark->monitor()方法獲取指向ObjectMonitor的指針,并返回,說(shuō)明膨脹過(guò)程已經(jīng)完成;
4、如果當(dāng)前鎖處于膨脹中(上圖33-37行),說(shuō)明該鎖正在被其它線程執(zhí)行膨脹操作,則當(dāng)前線程就進(jìn)行自旋等待鎖膨脹完成,這里需要注意一點(diǎn),雖然是自旋操作,但不會(huì)一直占用cpu資源,每隔一段時(shí)間會(huì)通過(guò)os::NakedYield方法放棄cpu資源,或通過(guò)park方法掛起;如果其他線程完成鎖的膨脹操作,則退出自旋并返回;
5、如果當(dāng)前是輕量級(jí)鎖狀態(tài)(上圖58-138行),即鎖標(biāo)識(shí)位為 00,膨脹過(guò)程如下:
  1. 通過(guò)omAlloc方法,獲取一個(gè)可用的ObjectMonitor monitor,并重置monitor數(shù)據(jù);
  2. 通過(guò)CAS嘗試將Mark Word設(shè)置為markOopDesc:INFLATING,標(biāo)識(shí)當(dāng)前鎖正在膨脹中,如果CAS失敗,說(shuō)明同一時(shí)刻其它線程已經(jīng)將Mark Word設(shè)置為markOopDesc:INFLATING,當(dāng)前線程進(jìn)行自旋等待膨脹完成;
  3. 如果CAS成功,設(shè)置monitor的各個(gè)字段:_header、_owner和_object等,并返回;
6、如果是無(wú)鎖(中立,上圖150-186行),重置監(jiān)視器值;
monitor競(jìng)爭(zhēng)
當(dāng)鎖膨脹完成并返回對(duì)應(yīng)的monitor時(shí),并不表示該線程競(jìng)爭(zhēng)到了鎖,真正的鎖競(jìng)爭(zhēng)發(fā)生在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 }
1、通過(guò)CAS嘗試把monitor的_owner字段設(shè)置為當(dāng)前線程;
2、如果設(shè)置之前的_owner指向當(dāng)前線程,說(shuō)明當(dāng)前線程再次進(jìn)入monitor,即重入鎖,執(zhí)行_recursions ++ ,記錄重入的次數(shù);
3、如果之前的_owner指向的地址在當(dāng)前線程中,這種描述有點(diǎn)拗口,換一種說(shuō)法:之前_owner指向的BasicLock在當(dāng)前線程棧上,說(shuō)明當(dāng)前線程是第一次進(jìn)入該monitor,設(shè)置_recursions為1,_owner為當(dāng)前線程,該線程成功獲得鎖并返回;
4、如果獲取鎖失敗,則等待鎖的釋放;
monitor等待
monitor競(jìng)爭(zhēng)失敗的線程,通過(guò)自旋執(zhí)行ObjectMonitor::EnterI方法等待鎖的釋放,EnterI方法的部分邏輯實(shí)現(xiàn)如下:
 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     }
1、當(dāng)前線程被封裝成ObjectWaiter對(duì)象node,狀態(tài)設(shè)置成ObjectWaiter::TS_CXQ;
2、在for循環(huán)中,通過(guò)CAS把node節(jié)點(diǎn)push到_cxq列表中,同一時(shí)刻可能有多個(gè)線程把自己的node節(jié)點(diǎn)push到_cxq列表中;
3、node節(jié)點(diǎn)push到_cxq列表之后,通過(guò)自旋嘗試獲取鎖,如果還是沒(méi)有獲取到鎖,則通過(guò)park將當(dāng)前線程掛起,等待被喚醒,實(shí)現(xiàn)如下:
 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 self11         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() ;//當(dāng)前線程掛起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 to28         // 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 completes37         // 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 while42         // we were spinning.  That's harmless.  If we iterate and call park(),43         // park() will consume the event and return immediately and we'll44         // just spin again.  This pattern can repeat, leaving _succ to simply45         // spin on a CPU.  Enable Knob_ResetEvent to clear pending unparks().46         // Alternately, we can sample fired() here, and if set, forgo spinning47         // 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     }

4、當(dāng)該線程被喚醒時(shí),會(huì)從掛起的點(diǎn)繼續(xù)執(zhí)行,通過(guò)ObjectMonitor::TryLock嘗試獲取鎖,TryLock方法實(shí)現(xiàn)如下:

 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 == 110          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 }

其本質(zhì)就是通過(guò)CAS設(shè)置monitor的_owner字段為當(dāng)前線程,如果CAS成功,則表示該線程獲取了鎖,跳出自旋操作,執(zhí)行同步代碼,否則繼續(xù)被掛起;

monitor釋放

當(dāng)某個(gè)持有鎖的線程執(zhí)行完同步代碼塊時(shí),會(huì)進(jìn)行鎖的釋放,給其它線程機(jī)會(huì)執(zhí)行同步代碼,在HotSpot中,通過(guò)退出monitor的方式實(shí)現(xiàn)鎖的釋放,并通知被阻塞的線程,具體實(shí)現(xiàn)位于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/exit15        // 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 enter28      TEVENT (Inflated exit - recursive) ;29      return ;30    }
...省略...

1、如果是重量級(jí)鎖的釋放,monitor中的_owner指向當(dāng)前線程,即THREAD == _owner;
2、根據(jù)不同的策略(由QMode指定),從cxq或EntryList中獲取頭節(jié)點(diǎn),通過(guò)ObjectMonitor::ExitEpilog方法喚醒該節(jié)點(diǎn)封裝的線程,喚醒操作最終由unpark完成,實(shí)現(xiàn)如下:
 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 be15    // out-of-scope (non-extant).16    Wakee  = NULL ;17 18    // Drop the lock19    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 JVMTI30    if (ObjectMonitor::_sync_Parks != NULL) {31       ObjectMonitor::_sync_Parks->inc() ;32    }33 }

 

3、被喚醒的線程,繼續(xù)執(zhí)行monitor的競(jìng)爭(zhēng);

四.總結(jié)

本文重點(diǎn)介紹了Synchronized原理以及JVM對(duì)Synchronized的優(yōu)化。簡(jiǎn)單來(lái)說(shuō)解決三種場(chǎng)景:

1)只有一個(gè)線程進(jìn)入臨界區(qū),偏向鎖

2)多個(gè)線程交替進(jìn)入臨界區(qū),輕量級(jí)鎖

3)多線程同時(shí)進(jìn)入臨界區(qū),重量級(jí)鎖

 

 

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

 

參考:

《深入理解 Java 虛擬機(jī)》

JVM源碼分析之synchronized實(shí)現(xiàn)

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
面試官和我扯了半個(gè)小時(shí)的synchronized,最后他輸了
深入理解Java里的各種鎖(上)
輕量級(jí)鎖與偏向鎖
死磕Synchronized底層實(shí)現(xiàn)
一個(gè)想休息的線程:JVM到底是怎么處理鎖的?怎么不讓我阻塞呢?
深入學(xué)習(xí)synchronized
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服