不少人一提到鎖,天然第一個想到了synchronized,但一直不懂源碼實現,現特意追蹤到C++層來剝開synchronized的面紗。java
網上的不少描述大都不全,讓人看了不夠爽,看完本章,你將完全瞭解synchronized的核心原理。node
開啓本文以前先介紹2個概念react
爲了提升性能,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前綴提供的內存屏障效果)。
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來標識對象加鎖狀態。
你們都知道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引入了自適應自旋。自旋時間根據以前鎖自旋時間和線程狀態,動態變化,用以指望能減小阻塞的時間。
鎖升級:偏向鎖--》輕量級鎖--》重量級鎖
按照以前的HotSpot設計,每次加鎖/解鎖都會涉及到一些CAS操做(好比對等待隊列的CAS操做),CAS操做會延遲本地調用,所以偏向鎖的想法是一旦線程第一次得到了監視對象,以後讓監視對象「偏向」這個線程,以後的屢次調用則能夠避免CAS操做。
簡單的講,就是在鎖對象的對象頭(開篇講的對象頭數據存儲結構)中有個ThreaddId字段,這個字段若是是空的,第一次獲取鎖的時候,就將自身的ThreadId寫入到鎖的ThreadId字段內,將鎖頭內的是否偏向鎖的狀態位置1.這樣下次獲取鎖的時候,直接檢查ThreadId是否和自身線程Id一致,若是一致,則認爲當前線程已經獲取了鎖,所以不需再次獲取鎖,略過了輕量級鎖和重量級鎖的加鎖階段。提升了效率。
注意:當鎖有競爭關係的時候,須要解除偏向鎖,進入輕量級鎖。
每個線程在準備獲取共享資源時:
得到偏向鎖以下圖:
如上圖所示:
注意點:JVM加鎖流程
偏向鎖--》輕量級鎖--》重量級鎖
從左往右能夠升級,從右往左不能降級
前兩節講了synchronized鎖實現原理,這一節咱們從C++源碼來剖析synchronized。
同步:多個線程併發訪問共享資源時,保證同一時刻只有一個(信號量能夠多個)線程使用。
實現同步的方法有不少,常見四種以下:
1)臨界區(CriticalSection,又叫關鍵段):經過對多線程的串行化來訪問公共資源或一段代碼,速度快,適合控制數據訪問。進程內可用。
2)互斥量:互斥量用於線程的互斥。只能爲0/1。一個互斥量只能用於一個資源的互斥訪問,可跨進程使用。
3)信號量:信號線用於線程的同步。能夠爲非負整數,可實現多個同類資源的多線程互斥和同步。當信號量爲單值信號量是,也能夠完成一個資源的互斥訪問。可跨進程使用。
4)事件:用來通知線程有一些事件已發生,從而啓動後繼任務的開始,可跨進程使用。
synchronized的底層實現就用到了臨界區和互斥鎖(重量級鎖的狀況下)這兩個概念。
重點來了,以前在第一節中的圖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.cpp
的InterpreterRuntime::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; 只有當其它線程嘗試競爭偏向鎖時,持有偏向鎖的線程纔會釋放鎖,偏向鎖的撤銷由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::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 }
重量級鎖經過對象內部的監視器(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的指針,並返回,說明膨脹過程已經完成;
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 }
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 }
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成功,則表示該線程獲取了鎖,跳出自旋操做,執行同步代碼,不然繼續被掛起;
當某個持有鎖的線程執行完同步代碼塊時,會進行鎖的釋放,給其它線程機會執行同步代碼,在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 }
...省略...
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 虛擬機》