java对象包含了三个部分:对象头,实例数据和对齐填充。
对象头又存放了:markWord和class point。
classpoint :指向方法区,当前对象的类信息数据。
markword:存储了很多和当前对象运行时的数据:例如hashcode,锁状态标志,指向锁记录的指针。
锁标志位:主要用来区分锁的等级,无锁->偏向锁->轻量级锁->重量级锁;
synchronized的实现原理?
synchronized被编译后会成才monitorenter和monitorexit两个字节码指令,分别表示加锁和释放锁。
monitorenter和monitorexit都是基于Monitor实现的。所谓的Monitor其实是一种同步工具,也可以说是一种同步机制。在Java虚拟机(HotSpot)中,Monitor是由ObjectMonitor实现的,可以叫做内部锁,或者Monitor锁。
ObjectMonitor的信息如下:
ObjectMonitor() {
_header = NULL;
_count = 0; // 记录线程获取锁的次数
_waiters = 0,
_recursions = 0; //锁的重入次数
_object = NULL;
_owner = NULL; // 指向持有ObjectMonitor对象的线程
_WaitSet = NULL; // 处于wait状态的线程,会被加入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; // 处于等待锁block状态的线程,会被加入到该列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
synchronized的重量级锁讲解:
通过上文已知,monitor由Entry Set 和 Wait Set 两个等待区。
Entry Set:存放已经准备抢锁的线程。
Wait Set:存放执行wait等指令的线程。
当某一个线程抢到锁,那么Owner就会指向改线程。改线程可以调用notify方法,随机唤醒一个线程进入Entry Set 区准备抢锁。
java1.6之后,synchronized有4种状态,无锁,偏向锁,轻量级锁和重量级锁。
无锁:不对资源放入synchronized代码块中。
偏向锁:对资源进行加锁,但是实际运行中只有一个线程获得这个锁。此时锁标志位还是01跟无锁是一样的。仅仅修改是否偏向锁的标志位,从0改到1.
当存在多个线程来获取这个锁时。偏向锁会升级为轻量级锁。但是又一个问题来了?当锁升级为轻量级时如何判断线程和锁之间的绑定关系呢?
轻量级锁和重量级锁都将前30bit修改为 指向栈中记录锁的指针 。此时这个指针会指向虚拟机栈中开辟的lockRecord,lockRecord存放的是MarkWord的副本和owner指针。
线程通过CAS尝试获取锁,一旦获取成功,将复制该对象的markword到自己的lockRecord,并修改owner指针,指向该对象。从而实现了线程和锁之间的绑定。
轻量级锁是多个线程在不同时间访问共享资源。
如果多个线程在同一时刻抢夺锁时,便会升级为重量级锁,就需要用到monitor机制,即如上文所述完全由jvm控制。