synchronized 修饰一个代码块时
,编译后会在同步块的先后分别造成monitorenter和monitorexit这两个字节码指令
,而当虚拟机执行monitorenter指令时,会去尝试获取对象的锁,若是这个对象没有被锁定或者当前线程已经持有了这个对象的锁(可重入性),就会把锁的计数器加一,而在执行monitorexit指令时锁的计数器就会减一;Mark Word
),通常为32bit或64bit(由虚拟机的位数决定),当该对象处于未被锁定的状态时,MarkWord中有25bit用来存储对象的hashCode(这里猜想是为了便于找到对象?与HashMap相似,但没有深刻去了解),4bit用于存储对象的分代年龄(GC相关),2bit用于存储锁标志位,1bit默认为0;每一个实例对象都会拥有一个等待队列(即为每一个实例准备的线程休息室),当线程处于锁定状态时,其余线程须要等待获取这个锁时,会加入该对象的等待队列(即图中的EntryList),而后等待获取锁,即被Owner指针所指向,(若是此时被wait()方法挂起,则会进入WaitSet
),若是中间没有被挂起过,则最后会调用monitorexit()方法释放锁,并将锁的计数器减一;java
monitor对象 是由 ObjectMonitor()对象实现的
ObjectMonitor() {
_header = NULL;
_count = 0; //记录个数
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL; //处于wait状态的线程,会被加入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; //处于等待锁block状态的线程,会被加入到该列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
复制代码
此时就能够明白了,每一个对象对应的信息都是存储在这的;数组
monitor对象
的指针时,说明有另外一个线程竞争了这个对象致使锁膨胀(在变成重量级锁前,竞争锁的对象会适当自旋给定的次数,避免频繁的线程挂起和唤醒,由于Java虚拟机的线程是映射到操做系统核心态的线程的,因此每次对于线程的操做,都会须要将系统转至核心态,而这个开销是比较大的,而这也是重量级锁慢的主要缘由),因此后面等待锁的线程也要进入阻塞状态;finally代码块中
手动释放锁,而JVM会保证后者的锁释放;其次是由于如今二者的性能也比较接近;Stop The Wrold
,查看当前MarkWord中的线程是否还在运行,若是已经终止则将线程ID改成本身;若是以前的线程尚未中止运行,则须要解偏向锁,升级成轻量级锁;轻量级锁适用于保护的代码块执行速度很快,且预计不会发生线程冲突的场景