首先要解释清楚这几种锁的特征和区别,以及运行时候的变化,需要了解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 ; } ... }代码只能是勉强在看,而且结合了他人的博客的理解,但是大致能看懂一下它在做什么操作。
重量级锁的获取和释放,暂时没有看懂,等后续看懂了再来书写自己的理解吧