This article is the third article Sike Synchronized achieve the underlying content to achieve a lightweight lock.
Lightweight locks is not complicated, many of which in a biased locking article has been mentioned, there will be some overlap with the content herein.
Lightweight additional lock background and the basic process has been explained in the introduction. It is strongly recommended to read this article on the basis of two articles of read next.
This series of articles will HotSpot synchronized
lock achieve a comprehensive analysis, including biased lock, lock lightweight, heavyweight lock lock, unlock, lock escalation process principle and source code analysis, hoping to study in synchronized
the students the way of some help .
This paper is divided into two parts:
1. Lightweight lock acquisition process
2. Lightweight lock release procedure
I see the JVM version is jdk8u.
Lightweight lock acquisition process
Lightweight lock acquisition process began the following analysis, code bytecodeInterpreter.cpp # 1816 .
The CASE (_monitorenter): { OOP lockee = STACK_OBJECT (-1 ); ... IF (! Entry = NULL) { ... // code is omitted above fails if the CAS operation also called to the monitorenter :: InterpreterRuntime // Lightweight locking Traditional IF (! Success) { // build a non-lock state Mark Word displaced markOop displaced = lockee-> Mark () -> set_unlocked (); // set to go lock Record entry-> lock () - > set_displaced_header (displaced); BOOL call_vm = UseHeavyMonitors; IF(Atomic :: || call_vm cmpxchg_ptr (entry, lockee-> mark_addr (), displaced)! = displaced) { // If successful CAS Alternatively, the object is not representative of non-lock state of the lock, which is determined when the lock is not reentrant // ? Is Simple recursive This Case IT IF (call_vm && the Thread-> is_lock_owned ((address) displaced->! {clear_lock_bits ())) entry -> Lock () -> set_displaced_header (NULL); } the else { // CAS operation It fails to call the monitorenter CALL_VM (InterpreterRuntime :: the monitorenter (the THREAD, entry), for handle_exception to); } } } UPDATE_PC_AND_TOS_AND_CONTINUE ( . 1, -1 ); } the else { istate->set_msg(more_monitors); UPDATE_PC_AND_RETURN(0); // Re-execute } }
If the lock mode object is not biased toward or have other threads, it success
is false
. This time will build a lock-free state mark word
is set to Lock Record
go, we said Lock Record
objects stored in mark word
a field called Displaced Mark Word
.
If the current state of the lock is not a lock-free state, the CAS fails. If this is a re-entry lock, that directly Lock Record
is Displaced Mark Word
set null
.
We see a demo, repeated three times in the demo in acquiring the lock,
synchronized(obj){ synchronized(obj){ synchronized(obj){ } } }
Suppose lightweight lock state of the lock, the reaction of the FIG mark word
, and the thread stack Lock Record
state, can be seen to the right of the thread stack contains three points to the current lock of the object Lock Record
. Of which the most significant bit of the stack Lock Record
allocated for the first time to acquire a lock. Which Displaced Mark word
before the lock object is locked mark word
, after the membership weight assigned a lock on the thread stack Displaced Mark word
of null
the Lock Record
.
Why choose to add JVM thread stack Displaced Mark word
to null Lock Record
to indicate re-entry count it? First lock-in number is sure to re-record, because when you unlock the need to correspond to a lock, unlock times equal to the number of lock, the lock is released really, that is to say when you unlock the need to use heavy lock the number of times. A simple solution is to re-lock the frequency of the recording head in a subject mark word
but mark word
the size is limited, the information that has been stored no less. Another option is to create a just Lock Record
and which records the number of re-entry, the reasons Hotspot has not done so I guess considering the impact on efficiency: Each lock needs to get re-entrant to traverse the thread's stack to find the corresponding Lock Record
, and then modify it value.
So in the end Hotspot select each are added to obtain a lock Lock Record
to represent the locks re-entry.
Then take a look InterpreterRuntime::monitorenter
Methods
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem)) ... Handle h_obj(thread, elem->obj()); assert(Universe::heap()->is_in_reserved_or_null(h_obj()), "must be NULL or an object"); if (UseBiasedLocking) { // Retry fast entry if bias is revoked to avoid unnecessary inflation ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK); } else { ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK); } ... IRT_END fast_enter biased locking process in a paper has been analyzed, and if the current mode is biased toward the thread is still using locks that will lock mark word changed status lightweight lock, thread stack at the same time it will be biased in the lock Record modified to lightweight lock corresponding form. In the position of the code # biasedLocking.cpp 212 . // thread still alive then traverse the thread stack all of the Record Lock GrowableArray <MonitorInfo *> * cached_monitor_info = get_or_compute_monitor_info (biased_thread); BasicLock * highest_lock = NULL; for ( int i = 0; i <cached_monitor_info-> length (); i ++ ) { a MonitorInfo * = cached_monitor_info- mon_info> AT (I); // if we can find the corresponding description Lock Record thread deflecting still perform synchronization code block IF (mon_info-> owner () == obj) { . .. //You need to upgrade to a lightweight lock, to directly modify the Lock Record bias thread stack. To handle re-entry lock case, where the Displaced Mark Word Lock Record is set to null, a first reprocessing Lock Record will be in the following code markOop Mark markOopDesc :: = encode ((* BasicLock ) NULL); highest_lock mon_info- => lock (); highest_lock -> set_displaced_header (mark); } the else { ... } } IF (highest_lock =! NULL) { // modify a lock Record for the first non-lock state, then mark the obj word to point to the Lock Record pointer highest_lock-> set_displaced_header (unbiased_prototype); obj -> release_set_mark (markOopDesc :: encode (highest_lock)); ... } the else { ... }
We look at slow_enter
the process.
void ObjectSynchronizer :: slow_enter (obj the Handle, BasicLock * Lock, TRAPS) { markOop Mark = obj-> Mark (); the Assert (! mark-> has_bias_pattern (), "Should not See BIAS pattern here Wallpaper" ); // if it is non-locked state IF (mark-> is_neutral ()) { // set Displaced Mark Word and Word replacement target head Mark lock -> set_displaced_header (Mark); IF (Mark == (markOop) Atomic :: cmpxchg_ptr (lock, obj () -> mark_addr (), Mark)) { TEvent (slow_enter: Release stacklock); return ; } } the else IF(mark-> has_locker () && the Thread-> is_lock_owned ((address) mark-> Locker ())) { Assert (! = mark- Lock> Locker (), "MUST Not Re-Lock Lock The Same" ); Assert (! Lock = (* BasicLock) obj-> Mark (), "do not relock the Same, with BasicLock" ); // if it is re-entrant, Displaced Mark Word is set to null lock -> set_displaced_header (nULL); return ; } ... // come this far already explained that there is a need to lock multiple threads competing for the expansion heavyweight lock lock -> set_displaced_header (markOopDesc :: unused_mark ()); ObjectSynchronizer :: inflate (tHREAD, obj ()) -> Enter (the THREAD); }
Lightweight lock release procedure
The CASE (_monitorexit): { OOP lockee = STACK_OBJECT (-1 ); CHECK_NULL (lockee); // derefing apos lockee of ought to provoke Check Implicit null // Find Our Monitor slot BasicObjectLock limit * = istate-> monitor_base (); BasicObjectLock * most_recent = (BasicObjectLock *) istate-> stack_base is (); // from low to high traverse stack lock Record the while (most_recent =! limit) { // If lock Record the lock object is associated IF ((most_recent) -> obj () == lockee) { BasicLock * = most_recent- Lock> Lock (); markOop header = lock ->displaced_header (); // release the Lock Record most_recent-> set_obj (NULL); // if it is biased mode, just release the Lock Record enough. Otherwise, go lightweight heavyweight lock or lock release procedure IF (lockee-> Mark () ->! Has_bias_pattern ()) { BOOL call_vm = UseHeavyMonitors; // ! = NULL header description is not re-entrant, you will need to Displaced Mark Word CAS head to subject Word Mark IF (header! = NULL || call_vm) { IF (Atomic :: || call_vm cmpxchg_ptr (header, lockee-> mark_addr (), Lock)! = Lock) { // CAS failure or heavyweight lock will come here, first obj reduction, and then call a method monitorexit most_recent-> set_obj (lockee); CALL_VM(InterpreterRuntime::monitorexit(THREAD, most_recent), handle_exception); } } } //执行下一条命令 UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); } //处理下一条Lock Record most_recent++; } // Need to throw illegal monitor state exception CALL_VM(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD), handle_exception); ShouldNotReachHere(); }
When the lock release needs to be lightweight Displaced Mark Word
replacement to the object head mark word
in. If the CAS heavyweight lock fails or proceeds to InterpreterRuntime::monitorexit
process.
//%note monitor_1 IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorexit(JavaThread* thread, BasicObjectLock* elem)) Handle h_obj(thread, elem->obj()); ... ObjectSynchronizer::slow_exit(h_obj(), elem->lock(), thread); // Free entry. This must be done here, since a pending exception might be installed on //释放Lock Record elem->set_obj(NULL); ... IRT_END
monitorexit
End call slow_exit
the method, it is released Lock Record
.
void ObjectSynchronizer :: slow_exit (OOP Object, BasicLock * Lock, TRAPS) { fast_exit (Object, Lock, the THREAD); } void ObjectSynchronizer :: fast_exit (OOP Object, BasicLock * Lock, TRAPS) { ... markOop DHW = lock - > displaced_header (); markOop Mark; IF (DHW == NULL) { // reentrant lock, do nothing ... return ; } Mark = object-> Mark (); // if mark word == Displaced Mark Word i.e. lightweight lock, CAS Word replacement target head Mark IF (Mark == (markOop) lock) { Assert(dhw-> is_neutral (), "invariant" ); IF ((markOop) Atomic :: cmpxchg_ptr (DHW, object-> mark_addr (), Mark) == Mark) { TEvent (fast_exit: Release stacklock); return ; } } // come here Description heavyweight lock or unlock when competition occurs, call the exit method heavyweight lock after expansion. :: the inflate ObjectSynchronizer (the THREAD, Object) -> Exit ( to true , the THREAD); }
This method is not the first judge lightweight lock, the lock will be replaced if it is lightweight mark word
, or cruiserweight lock and call for the expansion exit
method, associated logic will explain heavyweight lock in the article.
I compiled a free Java Advanced information, covering Java, Redis, MongoDB, MySQL, Zookeeper, Spring Cloud, Dubbo distributed high concurrency and other tutorials, a total of 30G, needs its own collection.
Portal: https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q