你知道 Java 的偏向锁要被废弃掉了吗?
2021/02/01 · vran

前言

偏向锁是从 JDK1.6 引入的一种针对 synchronized 的锁优化技术,然而从 JDK 15 开始,这一特性被官方标记为废弃状态,如果还想继续使用的话需要通过 JVM 参数手动启用。

-XX:+UseBiasedLocking

那么问题来了,JDK15 为什么要废弃偏向锁呢?

什么是偏向锁?

在回答 Why 之前要先明白 What:什么是偏向锁?

在 Java 中可以使用 synchronized 来保证代码块同一时间只被一个线程访问(互斥),它是基于 Monitor Object 模式来实现的。

int i;

public synchronized void mutex() {
  i++;
}

public void mutext2() {
  synchronized(this) {
    i++;
  }
}

当线程请求进入临界区时都需要先获取一个 monitor 对象(类似于准入许可证),获取 monitor 对象是通过 compare-and-swap (CAS) 操作来实现的。

从 CPU 的角度来看,CAS 其实是一个开销很昂贵的操作,有没有什么方法可以避免呢?

我们先看一个 JAVA 团队观察到的现象:

在大多数对象的生命周期内,基本上只会有一个线程访问临界区

基于此可以得出一个优化方案:当某个线程首次访问临界区时记录下该线程的信息,当再有线程访问该临界区时判断是否是首次访问的线程

  • 如果是:就直接放行,这样就避免了通过 CAS 获取 monitor 的操作
  • 如果不是:就升级为轻量级锁

这个优化方案其实就是偏向锁了,在 JVM 的实际实现中,锁升级其实有 4 个状态,并且是只可升级不可降级。

无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁

为什么要废弃?

大人,时代变了

在过去,Java 应用通常使用的都是 HashTable、Vector 等比较老的集合库,这类集合库大量使用了 synchronized 来保证线程安全。

如果在单线程的情景下使用这些集合库就会有不必要的加锁操作,从而导致性能下降。

而偏向锁可以保证即使是使用了这些老的集合库,也不会产生很大的性能损耗,因为 JVM 知道访问临界区的线程始终是同一个,也就避免了加锁操作。

这一切都很美好,但是随着时代的变化,新的 Java 应用基本都已经使用了无锁的集合库,比如 HashMap、ArrayList 等,这些集合库在单线程场景下比老的集合库性能更好。

即使是在多线程场景下,Java 也提供了 ConcurrentHashMap、CopyOnWriteArrayList 等性能更好的线程安全的集合库。

综上,对于使用了新类库的 Java 应用来说,偏向锁带来的收益已不如过去那么明显,而且在当下多线程应用越来越普遍的情况下,偏向锁带来的锁升级操作反而会影响应用的性能

时代变了,成本却还在增加

在废弃偏向锁的提案 JEP374 中还提到了与 HotSpot 相关的一点

Biased locking introduced a lot of complex code into the synchronization subsystem and is invasive to other HotSpot components as well.

简单翻译就是偏向锁为整个「同步子系统」引入了大量的复杂度,并且这些复杂度也入侵到了 HotSpot 的其它组件。

这导致了系统代码难以理解,难以进行大的设计变更,降低了子系统的演进能力,

总结下来其实就是 ROI (投资回报率)太低了,考虑到兼容性,所以决定先废弃该特性,最终的目标是移除它。

后续如何兼容?

默认禁用偏向锁可能会导致一些 Java 应用的性能下降,所以 HotSpot 提供了显示开启偏向锁的命令

# 在 Java15 后,手动开启偏向锁在启动的时候会收到警告信息
-XX:+UseBiasedLocking

以下和偏向锁相关的命令参数仍然可以使用,但是虚拟机会列出对应的警告信息表示其已被废弃掉

-XX:BiasedLockingStartupDelay=__
-XX:BiasedLockingBulkRebiasThreshold=__
-XX:BiasedLockingBulkRevokeThreshold=__
-XX:BiasedLockingDecayTime=__
// 必须在 C2 下才能使用
-XX:+UseOptoBiasInlining

// 必须配合参数-XX:+UnlockDiagnosticVMOptions使用
// 并且只能加在其后才能生效
-XX:+PrintBiasedLockingStatistics
-XX:+PrintPreciseBiasedLockingStatistics

so,扶我起来,我还能学!

参考

  1. JEP 374: Disable and Deprecate Biased Locking
  2. open JDK15 Release
  3. Synchronization and Object Locking By Christian Wimmer
  4. Biased Locking in HotSpot
  5. 探索 Java 同步机制
over