Java--偏向锁/轻量级锁/重量级锁

首先要解释清楚这几种锁的特征和区别,以及运行时候的变化,需要了解Java对象头的一些知识,在Java对象头的Mark Word内就记录着这些不同锁的状态位。另外偏向锁---轻量级锁---重量级锁本文都需要依托synchronize进行理解和分析。另外也要参照网络上很多的资料。

1.对象头:

关于对象头的具体记录,可以参考这边:对象头

2.同步的原理:

关于JVM规范对于同步的解释,可以参考这边:monitorentry和monitorexit

1.数据存储的变化---(主要针对Mark Word)

1.1基础状态 

在运行期间,对象头内Mark Word内存储的数据会随着锁标志位的变化而变化,具体分为以下4种(基于32位虚拟机):


1.2 c++中的存储结构分析和探究

上图中的Mark Word是一个表格式的展示样式,也是我从虚拟机书中和网络上找来的,但是我还是去搜罗了一下c++中的结构数据,便于下面对于流程的分析。

1.2.1Mark Word例子解析图

下图是截取于markOop.hpp


67行: 指向一个线程的显示偏向锁。

68行:一个匿名偏向锁。

72行:轻量级锁

73行:无锁

74行:重量级锁

75行:GC时使用

1.2.2 markOop中的枚举位解析:

基于上图中的最后3位,下图给出了枚举解释。

我们稍加分析下几个值:

5 == 101 就是上图中的偏向锁。

3 == 11 GC时使用。


2.偏向锁

2.1偏向锁的解释: 

以下摘自《深入理解Java虚拟机》,关于偏向锁的理解,是有点复杂的,我先是把此书内的观点摘抄下来,然后对比着理解。后续如果是蓝色的字体就是摘抄的书中的原话。借此来理解和分析。

当锁对象第一次被线程获取的时候,虚拟机会把对象头中的标志位设置为01,即偏向模式。同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word中。如果操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作(例如,Locking,Unlocking,以及对Mark Word的update)。

当有另一个线程去尝试获取到这个锁时,偏向模式就宣告结束。根据锁对象目前是否处于被锁定状态,撤销偏向后恢复到未锁定状态或升级为轻量级锁定的状态。

2.2偏向锁的c++代码

以下代码摘自链接:monitorenter


2.2.1.此方法的入参:

1.1JavaThread(当前线程的值)

1.2BasicObjectLock基础对象锁(根据字面意思强行解释一波)

1.2.1 BasicObjectLock结构如下:私有对象为 BasicLock 的实例 lock 和 oop 的实例 _obj。

class BasicObjectLock {
  BasicLock _lock; 
  // object holds the lock;
  oop  _obj;   
}

1.2.2 BasicLock主要存储的是markoop对象,就是对象头数据。

class BasicLock {
    volatile markOop _displaced_header;
}

2.2.2. 偏向锁的流程获取核心方法:fast_enter

在monitorenter代码中,可以看见如果开启了偏向锁的功能,那么获取的时候就进入了fast_enter流程。fast_enter就是这一篇主要分析的代码,或许理解的不够,需要参考很多资料。此处我发现有两个比较好的博客,稍作记录,两篇博客的观点有点差别,具体还是见仁见智吧,因为关于fast_enter的cpp代码我也读不懂~~~~~~~~~

解释一:

1.偏向锁的获取:


2.偏向锁的撤销:


解释二:


个人比较倾向解释一的观点,根据大致源码,发现解释二中在判断是否是偏向锁的否流程中,源码呢看起来是倾向获取轻量级锁,但是此图中确是去做CAS操作,这边有点疑惑。。。

3.轻量级锁的获取和释放

3.1源码简单分析

void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
	//获取mark word
  markOop mark = obj->mark();
  assert(!mark->has_bias_pattern(), "should not see bias pattern here");
  	
  if (mark->is_neutral()) {
	  //判断是否是不可偏向状态并且无锁 0 01
    // Anticipate successful CAS -- the ST of the displaced mark must
    // be visible <= the ST performed by the CAS.
    lock->set_displaced_header(mark);  //将mark word写入线程栈中的hdr中
	
    if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
	  //CAS 设置mark word指向hdr,保证只有一个线程能够设置成功
      TEVENT (slow_enter: release stacklock) ;
      return ;
    }
    // Fall through to inflate() ...
  } else
  if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
	  //当前mark word指向本地线程栈,并且和当前线程一致,保证重入的特性
    assert(lock != mark->locker(), "must not re-lock the same lock");
    assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock");
    lock->set_displaced_header(NULL);
    return;
  }

#if 0
  // The following optimization isn't particularly useful.
  if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) {
    lock->set_displaced_header (NULL) ;
    return ;
  }
#endif

  // The object header will never be displaced to this lock,
  // so it does not matter what the value is, except that it
  // must be non-zero to avoid looking like a re-entrant lock,
  // and must not look locked either.
  lock->set_displaced_header(markOopDesc::unused_mark());
  //膨胀成重量级锁
  ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}

3.2必备理论知识

3.2.1轻量级锁的获取当中有个很重要的概念,即线程栈帧中的锁记录(Lock Record),下图取自周志明-虚拟机一书


3.2.2 Stack中hdr和Lock Record存在的几个关键操作步骤如下图:


操作1:虚拟机会在线程栈帧中建立一个Lock Record,用于存储锁对象目前的Mark Word。

操作2:虚拟机使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针。

操作3:如果CAS成功了,将owner执向Mark Word,代表了这个线程拥有了该对象的锁。

3.2.3 基本流程分析


4.重量级锁的膨胀

ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
  // Inflate mutates the heap ...
  // Relaxing assertion for bug 6320749.
  assert (Universe::verify_in_progress() ||
          !SafepointSynchronize::is_at_safepoint(), "invariant") ;

  for (;;) {   //自旋操作
      const markOop mark = object->mark() ;
      assert (!mark->has_bias_pattern(), "invariant") ;

      // The mark can be in one of the following states:
      // *  Inflated     - just return (膨胀完成)
      // *  Stack-locked - coerce it to inflated (轻量级锁)
      // *  INFLATING    - busy wait for conversion to complete (膨胀中)
      // *  Neutral      - aggressively inflate the object. (无锁)
      // *  BIASED       - Illegal.  We should never see this(偏向锁)

      // CASE: inflated
      if (mark->has_monitor()) {
		  //如果是重量级锁,直接返回
          ObjectMonitor * inf = mark->monitor() ;
          assert (inf->header()->is_neutral(), "invariant");
          assert (inf->object() == object, "invariant") ;
          assert (ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is invalid");
          return inf ;
      }

      if (mark == markOopDesc::INFLATING()) {
		  //如果是膨胀中,自旋,等待膨胀完成
         TEVENT (Inflate: spin while INFLATING) ;
         ReadStableMark(object) ;
         continue ;
      }

      if (mark->has_locker()) {
          ObjectMonitor * m = omAlloc (Self) ;
          // Optimistically prepare the objectmonitor - anticipate successful CAS
          // We do this before the CAS in order to minimize the length of time
          // in which INFLATING appears in the mark.
		  
		  //如果当前是轻量级锁,获取自旋次数,验证是否应该升级为重量级锁
          m->Recycle();
          m->FreeNext      = NULL ;
          m->_Responsible  = NULL ;
          m->OwnerIsThread = 0 ;
          m->_recursions   = 0 ;
          m->_SpinDuration = Knob_SpinLimit ;   // Consider: maintain by type/class

          markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ;
          if (cmp != mark) {
             omRelease (Self, m) ;
             continue ;       // Interference -- just retry
          }

          
          markOop dmw = mark->displaced_mark_helper() ;
          assert (dmw->is_neutral(), "invariant") ;

          // Setup monitor fields to proper values -- prepare the monitor
          m->set_header(dmw) ;

          // Optimization: if the mark->locker stack address is associated
          // with this thread we could simply set m->_owner = Self and
          // m->OwnerIsThread = 1.  Note that a thread can inflate an object
          // that it has stack-locked -- as might happen in wait() -- directly
          // with CAS.  That is, we can avoid the xchg-NULL .... ST idiom.
          m->set_owner (mark->locker());
          m->set_object(object);
          // TODO-FIXME: assert BasicLock->dhw != 0.

          // Must preserve store ordering. The monitor state must
          // be stable at the time of publishing the monitor address.
          guarantee (object->mark() == markOopDesc::INFLATING(), "invariant") ;
          object->release_set_mark(markOopDesc::encode(m));

          // Hopefully the performance counters are allocated on distinct cache lines
          // to avoid false sharing on MP systems ...
          if (_sync_Inflations != NULL) _sync_Inflations->inc() ;
          TEVENT(Inflate: overwrite stacklock) ;
          if (TraceMonitorInflation) {
            if (object->is_instance()) {
              ResourceMark rm;
              tty->print_cr("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
                (intptr_t) object, (intptr_t) object->mark(),
                Klass::cast(object->klass())->external_name());
            }
          }
          return m ;
      }

      ...
}
代码只能是勉强在看,而且结合了他人的博客的理解,但是大致能看懂一下它在做什么操作。


重量级锁的获取和释放,暂时没有看懂,等后续看懂了再来书写自己的理解吧

猜你喜欢

转载自blog.csdn.net/qq_32924343/article/details/79680681