java中的锁是针对对象而言的,它锁住的是一个对象,并且具有可重入的性质。
java中的对象内存结构如图所示
普通对象内存结构:
数组对象内存结构:
其中关于锁状态的记录主要存储在_mark(markword)中,markword的结构如下以64位为例:
最开始的synchronized的实现是直接启用重量级锁,这样对于效率的影响是比较大,在后来的改进中引入了锁升级的概念,来增加执行的效率
public synchronized void testSync() {
synchronized(this){
System.out.println("test");
}
}
这个synchronized锁住的是包含当前方法的对象,当一个线程(Thread_1)来调用这个方法的时候,就可能会触发锁升级的过程,如下图所示:
自旋锁也叫轻量级锁,一般通过cas算法实现。
注意:锁只会升级不会降级
1、当Thread-1访问对象的时候,首先通过cas操作去获取偏向锁并将锁的偏向位更改为1;
2、当另一个线程(thread-2)到达的时候会比较自身线程id和对象头中id是否一致,发现不一致就会去检测对象头中的线程是否存活,如果Thread-1还是存活的就升级为轻量级锁;
3、如果获取失败则说明存在竞争关系,这时候将偏向锁升级为轻量级锁;升级为轻量级锁之后会在thread-2线程的栈帧中开辟一块锁记录空间叫做displaced Mark Word,并将锁对象的markword拷贝到线程本身的displaced Mark Word空间中,然后通过cas的方式去设置锁对像中线程id指针,并将锁的标志设置为00;
4、当其中一个线程的自旋次数超过阈值(默认是10)的时候为了防止cpu空转,会将自旋锁升级为重量级锁,将对象监视器的指针存储在对象头之中。
对象监视器结构如图
// hotspot中的对象监视器
ObjectMonitor() {
_count = 0; //用来记录该对象被线程获取锁的次数
_waiters = 0;
_recursions = 0; //锁的重入次数
_owner = NULL; //指向持有ObjectMonitor对象的线程
_WaitSet = NULL; //处于wait状态的线程,会被加入到_WaitSet
_WaitSetLock = 0 ;
_EntryList = NULL ; //处于等待锁block状态的线程,会被加入到该列表
}
在升级为重量级锁之后会创建一个ObjectMonitor()对象,并在锁对象的markword中记录下objectMonitor的指针;
在ObjectMonitor中有两个队列(entryList waitSet)和一个指针(owner),两个队列可以称为锁池和等待池,指针中记录的是当前拥有锁的线程;