前言
在看synchronized底层实现的八股的时候,发现对JVM底层优化的思路,大多数博客只是基本介绍了无锁,偏向锁,轻量锁和重量锁四种锁状态,并没有看到详细给出锁升级的步骤的文章,部分文章也只是描述了一个锁升级成另一个锁的部分,并没有完整的串起来。因此自己参考多篇文章的思路整理了一下整个转换的过程。
申请锁的步骤
首先,了解一下不同锁状态在对象头的Mark Word中对应的结构。
在任何情况下,对象一定处于这四种状态中的一种(也有可能状态位为11,用于GC,不能再被获取,所以此处不讨论),那么在线程访问synchronized修饰的代码时,首先就会读取Mark Word中的内容,查看当前的锁状态。
- 若锁状态位为10,则执行重量级锁的MonitorEnter和MonitorExit命令来进行加锁/解锁。
- 若锁状态位为00,则当前有线程持有该对象的轻量级锁,那么申请锁的线程需要自旋等待,若等待成功,则获得锁,若等待失败,则将轻量级锁升级为重量级锁。
- 若锁的状态位为01,则此时对象的状态是无锁或者偏向锁,下面使用流程图来说明具体的过程。
- 判断是否允许偏向锁以及是否存在偏向锁
- 若不支持偏向锁,则直接获取轻量锁
- 若无偏向锁则使用CAS获取偏向锁
- 若偏向锁是自己的则可以直接运行代码
- 否则,存在别的线程占用偏向锁(假设占用锁的线程为A,当前线程为B),需要进行偏向锁的撤销
- 等待线程A进入全局安全点
- 暂停线程A,判断A是否在执行当前锁区域代码
- 若是,则发生锁冲突,将偏向锁升级为轻量锁,由线程A去申请该轻量锁,线程B自旋等待轻量锁
- 若不是,则未发生冲突,线程B使用CAS去获取偏向锁
- 申请轻量锁的步骤
- 在线程的栈中开辟一个Record Lock的空间
- 将Mark Word的内容拷贝到Record Lock中
- 使用CAS修改Mark Word中的内容:
- 将Mark Word前62位替换为指向自己线程栈中的Record Lock的指针
- 将Mark Word最后2位的锁状态位改为00
- 若申请轻量锁时,CAS失败,则将轻量锁升级为重量锁;若成功,则执行代码,并在退出时,使用CAS将Record Lock中的内容拷贝回Mark Word,并修改状态位。若此时轻量锁已经被升级重量级锁,则唤醒锁等待的线程。