java Thread sleep 和obj.wait,以及sychronized,minor源码
sleep()方法是Thread類里面的,主要的意義就是讓當前線程停止執行,讓出cpu給其他的線程,但是不會釋放對象鎖資源以及監控的狀態,當指定的時間到了之后又會自動恢復運行狀態。
wait()方法是Object類里面的,主要的意義就是讓線程放棄當前的對象的鎖,進入等待此對象的等待鎖定池,只有針對此對象調動notify方法后本線程才能夠進入對象鎖定池準備獲取對象鎖進入運行狀態。
在JAVA中,是沒有類似于PV操作、進程互斥等相關的方法的。JAVA的進程同步是通過synchronized()來實現的,需要說明的是,JAVA的synchronized()方法類似于操作系統概念中的互斥內存塊,在JAVA中的Object類型中,都是帶有一個內存鎖的,在有線程獲取該內存鎖后,其它線程無法訪問該內存,從而實現JAVA中簡單的同步、互斥操作。明白這個原理,就能理解為什么synchronized(this)與synchronized(static XXX)的區別了,synchronized就是針對內存區塊申請內存鎖,this關鍵字代表類的一個對象,所以其內存鎖是針對相同對象的互斥操作,而static成員屬于類專有,其內存空間為該類所有成員共有,這就導致synchronized()對static成員加鎖,相當于對類加鎖,也就是在該類的所有成員間實現互斥,在同一時間只有一個線程可訪問該類的實例。如果只是簡單的想要實現在JAVA中的線程互斥,明白這些基本就已經夠了。但如果需要在線程間相互喚醒的話就需要借助Object.wait(), Object.nofity()了。
??? Obj.wait(),與Obj.notify()必須要與synchronized(Obj)一起使用,也就是wait,與notify是針對已經獲取了Obj鎖進行操作,從語法角度來說就是Obj.wait(),Obj.notify必須在synchronized(Obj){...}語句塊內。從功能上來說wait就是說線程在獲取對象鎖后,主動釋放對象鎖,同時本線程休眠。直到有其它線程調用對象的notify()喚醒該線程,才能繼續獲取對象鎖,并繼續執行。相應的notify()就是對對象鎖的喚醒操作。但有一點需要注意的是wait()調用后,并不是馬上就釋放對象鎖的,而是在相應的synchronized(){}語句塊執行結束,自動釋放鎖后,JVM會在wait()對象鎖的線程中隨機選取一線程,賦予其對象鎖,喚醒線程,繼續執行。這樣就提供了在線程間同步、喚醒的操作。Thread.sleep()與Object.wait()二者都可以暫停當前線程,釋放CPU控制權,主要的區別在于Object.wait()在釋放CPU同時,釋放了對象鎖的控制。
了解jvm 對象內存布局和鎖、監視器的cpp源碼:https://www.cnblogs.com/dennyzhangdd/p/6734638.html
sychronnied原理:
同步和互斥
同步:多個線程并發訪問共享資源時,保證同一時刻只有一個(信號量可以多個)線程使用。
實現同步的方法有很多,常見四種如下:
1)臨界區(CriticalSection,又叫關鍵段):通過對多線程的串行化來訪問公共資源或一段代碼,速度快,適合控制數據訪問。進程內可用。
2)互斥量:互斥量用于線程的互斥。只能為0/1。一個互斥量只能用于一個資源的互斥訪問,可跨進程使用。
3)信號量:信號線用于線程的同步。可以為非負整數,可實現多個同類資源的多線程互斥和同步。當信號量為單值信號量是,也可以完成一個資源的互斥訪問??煽邕M程使用。
4)事件:用來通知線程有一些事件已發生,從而啟動后繼任務的開始,可跨進程使用。
synchronized的底層實現就用到了臨界區和互斥鎖(重量級鎖的情況下)這兩個概念。
synchronized? C++源碼
重點來了,之前在第一節中的圖1,看過了對象頭Mark Word?,F在我們從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?
先看一下入參:
1、JavaThread thread指向java中的當前線程;
2、BasicObjectLock基礎對象鎖:包含一個BasicLock和一個指向Object對象的指針oop。
3、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方法實現,由于實現比較長,就不貼代碼了,實現邏輯如下:1、通過markOop mark = obj->mark()獲取對象的markOop數據mark,即對象頭的Mark Word;
2、判斷mark是否為可偏向狀態,即mark的偏向鎖標志位為?1,鎖標志位為?01;
3、判斷mark中JavaThread的狀態:如果為空,則進入步驟(4);如果指向當前線程,則執行同步代碼塊;如果指向其它線程,進入步驟(5);
4、通過CAS原子指令設置mark中JavaThread為當前線程ID,如果執行CAS成功,則執行同步代碼塊,否則進入步驟(5);
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 }1、偏向鎖的撤銷動作必須等待全局安全點;
2、暫停擁有偏向鎖的線程,判斷鎖對象是否處于被鎖定狀態;
3、撤銷偏向鎖,恢復到無鎖(標志位為?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 }1、markOop mark = obj->mark()方法獲取對象的markOop數據mark;
2、mark->is_neutral()方法判斷mark是否為無鎖狀態:mark的偏向鎖標志位為?0,鎖標志位為?01;
3、如果mark處于無鎖狀態,則進入步驟(4),否則執行步驟(6);
4、把mark保存到BasicLock對象的_displaced_header字段;
5、通過CAS嘗試將Mark Word更新為指向BasicLock對象的指針,如果更新成功,表示競爭到鎖,則執行同步代碼,否則執行步驟(6);
6、如果當前mark處于加鎖狀態,且mark中的ptr指針指向當前線程的棧幀,則執行同步代碼,否則說明有多個線程競爭輕量級鎖,輕量級鎖需要膨脹升級為重量級鎖;
假設線程A和B同時執行到臨界區if (mark->is_neutral()):
1、線程AB都把Mark Word復制到各自的_displaced_header字段,該數據保存在線程的棧幀上,是線程私有的;
2、Atomic::cmpxchg_ptr原子操作保證只有一個線程可以把指向棧幀的指針復制到Mark Word,假設此時線程A執行成功,并返回繼續執行同步代碼塊;
3、線程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 } 1、確保處于偏向鎖狀態時不會執行這段邏輯;2、取出在獲取輕量級鎖時保存在BasicLock對象的mark數據dhw;
3、通過CAS嘗試把dhw替換到當前的Mark Word,如果CAS成功,說明成功的釋放了鎖,否則執行步驟(4);
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 }膨脹過程的實現比較復雜,大概實現過程如下:1、整個膨脹過程在自旋下完成;
2、mark->has_monitor()方法判斷當前是否為重量級鎖(上圖18-25行),即Mark Word的鎖標識位為?10,如果當前狀態為重量級鎖,執行步驟(3),否則執行步驟(4);
3、mark->monitor()方法獲取指向ObjectMonitor的指針,并返回,說明膨脹過程已經完成;
4、如果當前鎖處于膨脹中(上圖33-37行),說明該鎖正在被其它線程執行膨脹操作,則當前線程就進行自旋等待鎖膨脹完成,這里需要注意一點,雖然是自旋操作,但不會一直占用cpu資源,每隔一段時間會通過os::NakedYield方法放棄cpu資源,或通過park方法掛起;如果其他線程完成鎖的膨脹操作,則退出自旋并返回;
5、如果當前是輕量級鎖狀態(上圖58-138行),即鎖標識位為?00,膨脹過程如下:
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 } 1、通過CAS嘗試把monitor的_owner字段設置為當前線程;
2、如果設置之前的_owner指向當前線程,說明當前線程再次進入monitor,即重入鎖,執行_recursions ++ ,記錄重入的次數;
3、如果之前的_owner指向的地址在當前線程中,這種描述有點拗口,換一種說法:之前_owner指向的BasicLock在當前線程棧上,說明當前線程是第一次進入該monitor,設置_recursions為1,_owner為當前線程,該線程成功獲得鎖并返回;
4、如果獲取鎖失敗,則等待鎖的釋放;
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 } 1、當前線程被封裝成ObjectWaiter對象node,狀態設置成ObjectWaiter::TS_CXQ;2、在for循環中,通過CAS把node節點push到_cxq列表中,同一時刻可能有多個線程把自己的node節點push到_cxq列表中;
3、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 }
4、當該線程被喚醒時,會從掛起的點繼續執行,通過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 } ...省略... 1、如果是重量級鎖的釋放,monitor中的_owner指向當前線程,即THREAD == _owner;2、根據不同的策略(由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 }
?
3、被喚醒的線程,繼續執行monitor的競爭;
參考:https://www.jianshu.com/p/c5058b6fe8e5
總結
以上是生活随笔為你收集整理的java Thread sleep 和obj.wait,以及sychronized,minor源码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TP-LINK TL-WR842+ 无线
- 下一篇: 美丽的龙泉山