JVM 对 synchronized 的优化

1 前言

我们都知道,synchronized 是重量级锁,其运行速度一直不敢恭维,万幸,JDK 1.6 之后为了减少获得锁和释放锁带来的性能消耗,引入了偏向锁和轻量级锁。

锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级为重量级锁,为了提高获得锁和释放锁的效率,锁的升级是单向的,即只有升级而不存在降级。

在这里插入图片描述

2 偏向锁

2.1 什么是偏向锁

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

2.2 偏向锁的核心思想

如果一个线程获得了锁,那么锁就进入偏向模式,此时 Mark Word 的结构也变为偏向锁结构,当这个线程再次请求锁时,无需再做任何同步操作。这样就省去了大量有关锁申请的操作,从而也就提高程序的性能。

2.3 偏向锁获取锁的流程

  1. 当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程 ID,以后该线程在进入和退出同步块时不需要进行 CAS 操作来加锁和解锁,只需简单地测试一下对象头的 Mark Word 里是否存储着指向当前线程的偏向锁即可
  2. 如果测试成功,表示线程已经获得了锁。如果测试失败,则需要再测试一下 Mark Word 中偏向锁的标识是否设置成1(表示当前是偏向锁)
  3. 如果没有设置,则使用 CAS 竞争锁,如果设置了就尝试使用 CAS 将对象头的偏向锁指向当前线程

2.4 偏向锁升级为轻量锁

如果偏向锁失败,虚拟机并不会立即挂起线程,而是会使用轻量级锁进行操作。

2.5 偏向锁的使用场景

对于没有锁竞争的场合,偏向锁有很好的优化效果;对于锁竞争比较激烈的场合,由于可能每次申请锁的线程都是不相同的,使用偏向锁很容易得不偿失。

3 轻量级锁

3.1 什么是轻量级锁

轻量级锁只是简单的将对象头部作为指针,指向持有锁的线程堆栈的内部,来判断一个线程是否持有对象锁。

3.2 轻量级锁的核心思想

对绝大部分的锁,在整个同步周期内都不存在竞争。

3.3 轻量锁升级为重量级锁

如果线程获得轻量级锁成功,则可以顺利进入临界区。如果轻量级锁加锁失败,则表示其他线程抢先夺到锁,那么当前线程的轻量级锁就会膨胀为重量级锁。

3.4 轻量级锁的使用场景

轻量级锁所适应的场景是线程交替执行同步块的场合,如果存在同一时间访问同一锁的场合,就会导致轻量级锁膨胀为重量级锁。

4 自旋锁

4.1 什么是自旋锁

自旋锁会假设在不久的将来,当前的线程可以获得锁,因此虚拟机会让当前想要获取锁的线程做几个空循环,一般不会太久,可能是50个循环或100个循环。在经过若干次循环后,如果得到锁,就顺利进入临界区。如果还不能获得锁,那就会将线程在操作系统层面挂起,最后没有办法就只能升级为重量级锁了。

4.2 自旋锁执行的条件

轻量级锁失败后,虚拟机为了避免线程在操作系统层面挂起,会执行自旋锁。

4.3 自旋锁的核心思想

在大多数情况下,线程持有锁的时间都不会太长,如果直接挂起操作系统层面的线程可能会得不偿失,毕竟操作系统实现线程之间的切换时需要从用户态转换到核心态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,所以可以试试执行自旋锁看看能不能很快获得锁。

4.4 为什么锁的升级无法逆向

自旋锁不知道到底会空循环几个时钟周期,这种方法很消耗 CPU,为了避免这种无用的自旋操作,一旦锁升级为重量锁,就不会再恢复到轻量级锁。

5 三种锁之间的对比

在这里插入图片描述

6 锁消除

Java 虚拟机在 JIT 编译时,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过这种方式消除没有必要的锁,可以节省毫无意义的请求锁时间,这就是锁消除。

我们看下面的代码

public void add(String str1, String str2) {
    StringBuffer sb = new StringBuffer();
    sb.append(str1).append(str2);
}

StringBuffer 中的 append 是一个同步方法,然而 StringBuffer 在方法中只是一个局部变量,不会被其他线程所使用,不可能存在共享资源竞争的情景,JVM 会将锁消除。

参考:深入理解Java并发之synchronized实现原理
Java多线程编程-(11)-从volatile和synchronized的底层实现原理看Java虚拟机对锁优化所做的努力

发布了113 篇原创文章 · 获赞 206 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Geffin/article/details/103516605
今日推荐