开启偏向锁一定性能更好吗?

Posted 明明如月学长

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了开启偏向锁一定性能更好吗?相关的知识,希望对你有一定的参考价值。

一、背景

最近工作中遇到由于使用偏向锁导致性能下降的案例。
趁机总结下偏向锁的概念和锁的升级过程,以及重点聊下偏向锁是否会让性能更优化。

二、偏向锁

偏向锁是Java 6之后加入的一种针对加锁操作的优化手段,它是基于:在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得。因此,为了减少同一线程获取锁的代价(会涉及到一些CAS操作,耗时),引入了偏向锁。

偏向锁的工作原理是:当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程再次进入同步块时,无需再做任何同步操作。这样就省去了大量有关锁申请和释放的操作,从而提高程序的运行效率。

当有另一个线程试图访问同步块时,偏向模式就宣告结束。根据对象头里记录的信息判断是否需要撤销偏向。如果需要撤销,则等待原来的线程进入安全点(safepoint),然后暂停它,并清除对象头和栈帧中的相关信息。撤销之后重新竞争获取轻量级锁。

三、锁升级

锁的升级过程是这样的:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁。这个过程是不可逆的,这么做是为了提高同步的性能。

偏向锁阶段
当一个线程第一次访问一个同步块时,会在对象头和栈帧中记录线程ID,这时对象处于偏向锁状态。如果后续没有其他线程竞争该对象,那么该线程再次进入同步块时就无需再做任何同步操作。

升级轻量级锁
如果有另一个线程试图获取该对象的锁,那么偏向锁就会被撤销,并升级为轻量级锁。轻量级锁是通过 CAS 操作在对象头中设置一个指向栈中锁记录的指针来实现的。如果CAS 成功,那么当前线程就获得了轻量级锁;如果CAS失败,说明有多个线程竞争该对象,那么轻量级锁就会升级为重量级锁。

升级重量级锁
重量级锁是通过在对象头中设置一个指向重量级监视器(monitor)的指针来实现的。重量级监视器会阻塞所有未获得该对象的线程,并唤醒其中一个等待线程来获取该对象。这样就避免了多个线程不断自旋消耗CPU资源 。

特殊情况

hashCode 并不是初始就写在对象头的,而是第一次调用 hashCode 方法时写入的。
如果一个对象没有获取偏向锁前调用 hashCode 方法,则进入普通无锁状态并存储 hashCode。
当已经获取偏向锁后调用 hashCode 或 wait 方法,则直接竞争重量级锁。
详情参见:《HashCode方法的调用对Java锁的影响》

四、偏向锁性能更好?

通过前面的介绍,我们不难知道:

偏向锁性能更好的情况是,当一个对象只有一个线程访问,并且不会有其他线程竞争该对象时。这样,偏向锁可以完全取消同步操作,只需要在第一次获取锁时进行一次CAS操作,之后就可以直接进入同步块。

但是,如果一个对象经常被多个线程竞争,那么偏向锁就会频繁地撤销和恢复,增加了额外的开销。这种情况下,使用轻量级锁或重量级锁可能更合适。

不知道大家有没有留意自己的服务器的 JVM 参数配置,有些高并发的服务,开启偏向锁后会因为偏向锁频繁撤销导致系统停顿时间增加,偏向锁的撤销需要等待全局安全点(safe point),暂停持有偏向锁的线程,检查持有偏向锁的线程状态。

Java在JDK1.6 以后默认已经开启了偏向锁这个优化,JDK15 中,偏向锁被默认禁用了,偏向锁带来的加锁时性能提升从实际效果上看并不明显,不再推荐使用,最终将被废弃。

Biased locking introduced a lot of complex code into the synchronization subsystem and is invasive to other HotSpot components as well. This complexity is a barrier to understanding various parts of the code and an impediment to making significant design changes within the synchronization subsystem. To that end we would like to disable, deprecate, and eventually remove support for biased locking.

如果你想手动开启或关闭偏向锁,你可以使用 -XX:+UseBiasedLocking -XX:-UseBiasedLocking 参数来控制。

Applications with substantial amounts of uncontended synchronization may attain significant speedups while others with certain patterns of locking may see slowdowns.

一句话结论:一般来说面向C 端的,并发较高的应用,尽量关闭偏向锁;面向 B 端的,并发较低的应用,可以考虑开启偏向锁。

五、More

很多人说:“面试造火箭,入职拧螺丝”。
但工作中真正遇到疑难杂症时,还是这些造火箭的知识更能解决问题。
学习不是目的,学习的目的还是为了:”学以致用“。
大家学习某个技术时,要了解某项技术解决什么问题,优缺点是什么。

每个问题都是我们深入掌握某个知识点的绝佳机会。当你工作中遇到问题时,一定要有寻根究底的态度,趁机掌握好相关知识,才能不断增加技术深度。

学任何知识都要比一般人掌握更多才能更具有优势。对于偏向锁而言,不仅要知道概念,还要知道为什么提供这种特性,什么情况下使用,进一步了解锁的升级过程,了解锁升级的特殊情况等。

六、参考文章

[1] 《25 张图 | 深入浅出「偏向锁》」
[2] 《HashCode方法的调用对Java锁的影响》
[2] 《Java 6 性能白皮书》
[3] JEP 374: Deprecate and Disable Biased Locking

以上是关于开启偏向锁一定性能更好吗?的主要内容,如果未能解决你的问题,请参考以下文章

Java synchronized锁的底层实现概述

JVM中锁优化,偏向锁自旋锁锁消除锁膨胀

Synchronized锁性能优化偏向锁轻量级锁升级 多线程中篇

Java 有什么锁

Java锁优化思路及JVM实现

Java偏向锁实现原理(Biased Locking)