JVM锁升级的过程

JDK6对Synchronized进行了优化,不再默认是重量级锁,有了锁升级过程。

1、无锁 → 偏向锁

因为经过HotSpot的作者大量的研究发现,大多数时候是不存在锁竞争的,常常是一个线程多次获得同一个锁,因此如果每次都要竞争锁会增大很多没有必要付出的代价,为了降低获取锁的代价,才引入的偏向锁。

如果一块临界区从未被任何线程访问过,则它就一直处于无锁状态。当第一个线程来访问它时,这时候线程会判断锁状态标志位为01偏向标志位为0,表示无锁且可以使用偏向锁。这时就会启动加偏向锁的过程:

  • 将偏向标志位设置为1
  • 使用CAS操作将线程ID写入对象头

如果CAS操作成功,则该线程就获取到了偏向锁,该线程每次进入同步块时就再也不用加锁解锁了。如果CAS操作失败,说明发生了锁竞争,进入升级轻量级锁过程。

使用参数-XX:BiasedLockingStartupDelay可以设置偏向锁开启的延迟时间,默认为4秒,表示程序运行4秒后才会启用偏向锁。原因在于,系统刚启动时,一般数据竞争是比较激烈的,此时启用偏向锁会降低性能。未开启偏向锁时的锁升级过程为:无锁→轻量级锁→重量级锁。

2、偏向锁 → 轻量级锁

轻量级锁考虑的是竞争锁对象的线程不多,而且线程持有锁的时间也不长的情景。因为阻塞线程需要CPU从用户态转到内核态,代价较大,如果刚刚阻塞不久这个锁就被释放了,那这个代价就有点得不偿失了,因此这个时候就干脆不阻塞这个线程,让它自旋着等待锁释放。

当第二个线程尝试获取已被置为偏向的锁时,偏向模式就马上宣告结束。根据锁对象目前是否处于被锁定的状态来决定是否撤销偏向(偏向标志设为0),撤销后锁标志位恢复到未锁定或轻量级锁的状态。如果对象处于锁定状态,则直接将锁升级到重量级锁,否则由当前试图访问到线程加上轻量级锁。轻量级锁的加锁过程如下:

  • 将锁标志位设置为00,表示轻量级锁
  • 在当前线程的栈帧中开辟一片空间存储Mark Word
  • 通过CAS操作将对象头的信息替换为栈帧中存储备份的地址

3、轻量级锁 → 重量级锁

如果出现两个以上的线程争用同一个锁的情况,那轻量级锁就不再有效,必须要膨胀为重量级锁,锁标志位状态改为10,此时Mark Word中存储的就是指向重量级锁的指针,后面等待锁的线程也不许进入阻塞状态。

References

https://www.jianshu.com/p/ee02e710a8b9
https://zhuanlan.zhihu.com/p/187460479

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容