Synchronized let you read an article the underlying implementation, the interviewer spike

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 synchronizedlock 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 synchronizedthe 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 successis false. This time will build a lock-free state mark wordis set to Lock Recordgo, we said Lock Recordobjects stored in mark worda 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 Recordis  Displaced Mark Wordset 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 Recordstate, 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 Recordallocated for the first time to acquire a lock. Which Displaced Mark wordbefore the lock object is locked mark word, after the membership weight assigned a lock on the thread stack Displaced Mark wordof nullthe Lock Record.

"Sike Synchronized underlying implementation - Lightweight lock"

Why choose to add JVM thread stack Displaced Mark wordto null Lock Recordto 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 wordbut mark wordthe size is limited, the information that has been stored no less. Another option is to create a just Lock Recordand 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 Recordto represent the locks re-entry.

Then take a look InterpreterRuntime::monitorenterMethods

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_enterthe 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 Wordreplacement to the object head mark wordin. If the CAS heavyweight lock fails or proceeds to InterpreterRuntime::monitorexitprocess.

//%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

 

monitorexitEnd call slow_exitthe 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 exitmethod, 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

Guess you like

Origin www.cnblogs.com/yuxiang1/p/11316099.html