锁消除、锁粗化、偏向锁、适应性锁

文章讨论了Java虚拟机(JIT)对锁的几种优化策略,包括锁消除,通过逃逸分析减少不必要的锁操作;锁粗化,合并小的同步块以减少锁的开销;偏向锁,优化单线程持有的锁;以及适应性锁,根据锁的持有时间动态选择忙等或暂停策略,以平衡性能和资源消耗。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

锁消除

锁消除是JIT编译器对内部锁实现的一种优化。JIT可以借助逃逸分析来判断同步块的锁对象是否只是被一个线程访问,如果是的话,则在编译期间不生成内部锁的申请与释放对应的机器码,即消除了锁的使用。这种技术被称为锁消除。它可以减少锁的开销。
在这里插入图片描述
上面的代码在编译时,经过锁消除后,可以等效为下面的:
在这里插入图片描述

StringBuffer是线程安全的类,其append方法和toString方法都是用内部锁进行修饰的,但是当作为方法的局部变量时,因为只会有当前线程对该变量进行访问,所以JIT会进行锁的消除。
如下面代码,当JIT编译doSomething方法时,会将stringBuffer.append和 stringBuffer.toString方法复制到doSomething方法的方法体中,这种技术被成为内联。下面的代码,会对StringBuffer的append和toString方法进行锁的消除。

  private static void doSomething() {

        StringBuffer stringBuffer = new StringBuffer();

        stringBuffer.append("1");

        String s = stringBuffer.toString();
    }

注意:
并不是所有的方法都会进行锁消除,锁的消除技术依赖于JIT的内联优化,至于哪些方法会被内联,要取决于该方法的热度和该方法字节码的尺寸。

锁消除的好处是我们在需要使用锁的场景时,不用考虑锁的开销,只需要关注我们的业务逻辑即可,JIT会对我们的代码进行锁的消除。
但并不是所有方法都会进行锁消除,这个还是要取决于JIT。锁消除是默认开启的。

锁粗化

锁粗化也是JIT编译器对内部锁的一种优化。
在这里插入图片描述

如上面的代码,这些同步块使用的是同一把锁,则JIT会将这些同步块合并为一个大的同步块,从而减少了锁申请、释放的开销。上面的代码会被JIT粗化成如下代码:
在这里插入图片描述
相邻同步块之间如果存在其他语句,也不一定会阻碍JIT编译器进行锁的粗化,因为JIT编译器可能会在执行锁粗化前进行指定重排序,将这些语句排序到同一临界区内。
锁粗化可能会使一个线程持有一个内部锁的时间变长,从而导致其他线程申请该锁的时间也变长,阻塞了其他线程。因此,锁粗化不会被应用到循环体内的相邻同步块中。锁粗化是默认开启的。

偏向锁

偏向锁是java虚拟机对锁实现的一种优化。当大多数锁没有被争用时,并且在整个生命周期至多只会被一个线程持有,java虚拟机也会对其进行申请锁和释放锁,这就带来了昂贵的开销。因此,jvm会为每个对象维护一个偏好,即一个对象对应的内部锁第一次被一个线程获得时,则该线程就会被记录为该对象的偏好线程,这个线程在后续对该锁的访问都无需申请和释放锁,从而减少了开销。

然而,当该对象被偏好线程以外的线程申请锁时,jvm会回收该对象对原线程的偏好,并重新设置该对象的偏好线程,这个过程的代价也比较昂贵,因此,偏向锁的优化只适用于当前对象没有被大量线程争用的场景。
偏向锁的优化是默认开启的。

适应性锁

适应性锁是JIT编译器对内部锁实现所做的一种优化。
当一个线程获取某个对象的锁时,该锁如果被其他线程占用,则该线程会进行暂停,变为非Runnable状态,由于暂停会导致上下文切换,因此暂停这种方法适用于大多数线程对该锁持有时间比较长的场景,这样才能抵消上下文切换的开销。另外一种方式就是采用忙等,忙等就是当锁被占有时,当前线程不被阻塞,而是执行空的循环,直到获取到锁。
忙等的好处是不会导致上下文切换,但如果等待的时间太长,则会一直执行下去,从而消耗cpu资源。因此忙等适用于绝大多数线程对该锁的持有时间较短的场景。

jvm可以根据不同的场景来选择上面的两种策略。对于线程持有时间较长的锁,jvm会采用暂停的策略,对于线程持有时间较短的锁,jvm会采用忙等的策略。jvm也可能先采用忙等的策略,忙等失败的情况下再采用暂停的策略。jvm的这种优化被成为适应性锁

### 回答1: Java的原生是一种非常有效的同步机制,它可以在多线程环境中确保代码的线程安全。JVM对原生做了很多优,其中包括重量级偏向、轻量级的自旋,以及偏向和自旋的转换机制。此外,JVM还实现了偏向和轻量级偏向升级、偏向撤销和自旋撤销等技术,也支持同步器(Synchronizers)。这些优技术有助于提高系统的吞吐量,提升程序的性能。 ### 回答2: JVMJava的原生做了许多优,以提高多线程程序的性能和效率。 首先,JVM引入了偏向和轻量级的概念。偏向是指当只有一个线程访问同步块时,JVM会将的对象头标记为偏向状态,使得该线程可以在未来的同步操作中直接获取,而无需再次进行同步。这减少了对的竞争,并且减少了额外的同步操作,提高了程序的执行性能。 其次,JVM还引入了自旋。当一个线程尝试获取时,如果发现已经被其他线程占用,该线程并不会被挂起,而是继续进行忙等待,反复检查是否可用。这避免了线程挂起和恢复的开销,对于竞争不激烈的场景,可以提高程序的响应速度。 此外,JVM还对机制进行了一些优,例如消除消除是指在某些情况下,JVM会分析代码,发现某些是不必要的,可以将其消除,从而减少的竞争。是指在某些情况下,JVM会将多个连续的细粒度合并成一个粒度,减少的拆卸和重装的开销。 最后,JVM还引入了适应性自旋,也称为自适应自旋。适应性自旋是指JVM根据程序的历史执行情况和硬件条件来动态地调整自旋的次数和策略,以提高自旋的效率和减少对系统资源的浪费。 综上所述,JVMJava的原生进行了偏向、轻量级、自旋消除适应性自旋等优,以提高多线程程序的性能和效率,减少竞争和资源浪费。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值