java并发编程(七)synchronized原理之重量级锁
前文讲解轻量级锁时,当线程使用CAS尝试对对象加锁,有两种失败情况,一种情况是自己执行了synchronized锁重入;另外一种,就是本文需要学习的重点内容:锁膨胀。
一、何为锁膨胀?
轻量级锁是指在满足一定的条件内,使用CAS(自旋)来尝试获取对象锁的一种机制,如果超过以下条件,则会膨胀为重量级锁:
1)在jdk1.6前,默认10次,可通过-XX:PreBlockSpin来修改,或者自旋线程数超过CPU核数的一半。
2)jdk1.6之后,引入了自适应自旋锁,次数并非一成不变。根据获取锁的成功率来决定是否能有更长的等待时间。
假设当前Object对象,已经被Thread1所持有,当Thread2前来竞争这把锁,满足上述条件后,会发生锁膨胀,如下图所示:
![](https://img.haomeiwen.com/i16830368/27b8d7f75e84bceb.png)
如上所示,此时Thread2来获取轻量级锁肯定失败的,所以会进入锁膨胀的流程:
1)为Object对象申请Monitor锁,Object的Mark Word指向Monitor地址;Monitor的Owner指向Thread1的锁记录。
2)Thread2进入Monitor的EntryList当中,状态变成BLOCKED。
![](https://img.haomeiwen.com/i16830368/5aeb3832ea07c8e8.png)
当Thread1执行完代码块的内容后,开始释放锁,使用CAS去重置Object的Mark Word,此时会失败。因为当前对象头存储的是Monitor的地址。
所示此时会进入重量级锁的解锁过程。将Monitor的Owner设置为null,同时唤醒EntryList中的Thread-2。
二、自适应自旋锁
jdk1.6之后,引入了自适应自旋锁,在重量级锁当中,也进行了一些优化。
前面提到当发生锁膨胀后,没持有锁的线程会进入Monitor的EntryList当中进行阻塞,实际情况是,会通过自适应自旋锁进行一定次数的自旋,如果获取到锁了,就避免进入阻塞状态(会进行上下文切换)。如果没获取到锁,此时在进入阻塞状态。
- 自旋是占用CPU的,只有在多核CPU中才能发挥优势。
- 自适应自旋锁会动态调整自旋次数,获取锁成功的次数多,就会多自旋几次;如果一次都没有成功,则会可能会直接进行阻塞。
- java7后不能控制是否开启自旋锁。