'锁升级过程'


JVM中每一个对象都有一个对象头,用于保存对象的系统信息,对象头中有一个部分被称为Mark Word,它存放了对象的哈希值、对象年龄、锁的指针等信息。

如果将所有的线程竞争都交给操作系统处理,那么并发性能是非常低下的,因此虚拟机在操作系统挂起线程之前,会先尽一切可能在虚拟机层面解决竞争关系,尽可能避免真实的竞争发生。同时,在竞争不激烈的场合,也会试图消除不必要的竞争。

无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁	// 随着竞争的增加,只能锁升级,不能降级

1.偏向锁

偏向锁的思想是偏向于让第一个获取锁对象的线程,之后再次请求该锁不再需要同步操作。当有另外一个线程去尝试获取这个锁对象时,锁就退出偏向模式。

偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加 VM 参数 -XX:BiasedLockingStartupDelay=0 来禁用延迟。JDK 8 延迟 4s 开启偏向锁原因:在刚开始执行代码时,会有好多线程来抢锁,如果开偏向锁效率反而降低

2.轻量级锁

如果偏向锁获取失败,虚拟机就会让线程申请轻量级锁。

3.锁膨胀

当轻量级锁失败,虚拟机就会使用重量级锁,分为两步,首先通过inflate()方法进行锁膨胀,目的是获取对象的ObjectMonitor;然后使用enter()方法尝试进入该锁。

在调用enter()方法时,线程很可能会在操作系统层面被挂起,此时线程间切换和调度的成本就会比较高。

4.自旋锁

锁膨胀后,进入ObjectMonitor的enter()方法,线程很可能会在操作系统层面被挂起,这样线程上下文切换的性能损失就会比较大。使用自旋锁可以让线程尽快进入临界区避免被操作系统挂起。

5.锁消除


文章作者: Fuchanglai
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Fuchanglai !
赏
  目录