Synchronized 的实现原理

向量数据库大模型机器学习

picture.image

苏三的免费八股文网站:

www.susan.net.cn

在刚开始学习 Java 并发编程的过程中,一遇到多线程,我们就会使用 synchronized 关键字。

在 JDK1.5 之前,Synchronized 是一个重量级锁,效率不尽如人意。

JDK1.6 对 Synchronized 锁进行了升级优化,引入了偏向锁和轻量级锁,提高了获取锁和释放锁的效率。下面我们来看一看 Synchronized 的底层实现原理吧。

Synchronized 的底层实现原理

同步原理

我们先来反编译下面的 method1 方法:


        
        
            

          
 
 public
 
  
 
 void
 
  
 
 method1
 
 
 ()
 
  
 
          {
          
   

 
              
          
 synchronized
 
           (
          
 this
 
          ) {
          
   

 
                  System.out.println(
          
 "This is the synchronized"
 
          );
          
   

 
              }
          
   

 
          }
          
   

 
        
      

在下面,我们可以看到反编译后的 method1 代码:

picture.image

从上面代码执行过程中,我们看到代码块同步是使用 monitorenter 和 monitorexit 指令实现的。

monitorenter 指令是在编译后插入到同步代码块的开始位置,monitorexit 是插入到方法结束的位置或者异常处。

JVM 要保证每个 monitorenter 必须有对应的 monitorexit 与之配对。任何对象都有一个 monitor 对象与之关联,当 monitor 被对象持有后,它将处于锁定状态。线程执行到 monitorenter 指令时,将会尝试获取对象所对应的 monitor 的所有权,即尝试获得对象的锁。

我们再看一下 Synchronized 方法同步的步骤:


        
        
            

          public synchronized void method2() {
          
   

 
              System.out.println("This is the synchronized method");
          
   

 
          }
          
   

 
        
      

picture.image

在上面编译过的 method2() 方法的执行过程中,Synchronized 方法的同步是使用另外一种方式实现的,我们可以看到它被翻译成普通的方法调用和返回 invokevirtual、return 指令。

一个被 Synchronized 修饰的方法在 JVM 底层并没有与之对应的字节码指令。当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了执行线程将先获取 monitor,获取成功之后,才能执行方法,方法执行完成再释放 monitor。

Java 对象头的概念

接下来,我们再来了解一下 Java 对象头的概念。Synchronized 用的锁就是存放在 Java 对象头里。如果对象是数组类型,则虚拟机用 3 个字宽(Word)存储对象头,如果对象是非数组类型,则用 2 字宽存储对象头。在 32 位虚拟机中,1 字宽等于 4 字节,即 32bit。

我们看下面的表格,Java 对象头主要包括两部分数据:Mark Word(标记字段)、Class Pointer(类型指针)。Mark Word 用于存储对象自身的运行时数据,它是实现轻量级锁和偏向锁的关键。Class Pointer 是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

picture.image

Java 对象头里的 Mark Word 里默认存储对象的 HashCode、分代年龄和锁标记位。我们来看一下 32 位 JVM 的 Mark Word 的默认存储结构:

picture.image

在运行期间,Mark Word 里存储的数据会随着锁标志位的变化而变化。Mark Word 可能变化为存储以下 4 种数据,我们来看一下存储结构:

picture.image

在 64 位虚拟机下,Mark Word 是 64bit 大小的,我们看一下它的存储结构:

picture.image

我们从上面的表格中看到了引入的偏向锁和轻量级锁。锁级别从低到高依次为:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这 4 种状态是会随着竞争情况而逐渐升级的。锁可以升级但不能降级,目的是提高获得锁和释放锁的效率。

下面我们来了解一下锁升级的流程。

锁升级

Synchronized 内部有一个隐藏的锁升级流程,正是因为这个流程的存在,使得 Synchronized 得以发挥它的高性能特性。锁升级中最重要的 2 个升级就是偏向锁和轻量级锁,下面我们分别展开讨论:

偏向锁

通常情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程更容易获得锁而引入了偏向锁。

所谓偏向锁,就是当一个线程访问同步代码块并获取锁时,会在对象头存储锁偏向的线程 ID。这样,以后该线程在进入和退出同步块时,就不需要进行 CAS 操作来加锁和解锁,只需要简单地测试一下对象头的 Mark Word 里是否存储着指向当前线程的偏向锁。

如果测试成功,则表示线程已经获得了锁;如果测试失败,则需要再测试一下 Mark Word 中偏向锁的标识是否设置成 1(表示当前是偏向锁)。如果没有设置,则使用 CAS 竞争锁;如果设置了,则尝试使用 CAS 将对象头的偏向锁指向当前线程。

偏向锁在 Java6 和 Java7 是默认启用的,但它在应用程序启动几秒钟之后才会被激活,我们可以配置 JVM 参数来关闭延迟:-XX:BiasedLockingStartupDelay=0。如果确定应用程序里所有的锁通常情况下处于竞争状态,我们可以配置如下的 JVM 参数关闭偏向锁,之后程序默认会进入轻量级锁状态:


        
        
            

          -XX:-UseBiasedLocking=
          
 false
 
          
   

 
        
      

轻量级锁

轻量级锁不是用来替代传统的重量级锁的,而是在没有多线程竞争的情况下,使用轻量级锁能够减少性能消耗,但是当多个线程同时竞争锁时,轻量级锁会膨胀为重量级锁。

我们先来看一下轻量级锁的加锁过程。在线程在执行同步块之前,JVM 会先在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头中的 Mark Word 复制到锁记录中,官方称为 Displaced Mark Word。然后线程尝试使用 CAS 将对象头中的 Mark Word 替换为指向锁记录的指针。如果成功,当前线程获

得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

轻量级解锁时,会使用原子的 CAS 操作将 Displaced Mark Word 替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。

因为自旋会消耗 CPU,为了避免无用的自旋,一旦锁升级到重量级锁,就不会再恢复到轻量级锁状态。

当锁处于这个状态下,其他线程尝试获取锁时,都会被阻塞住,当持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程就会进行新一轮的竞争。

下图是两个线程同时争夺锁,导致轻量级锁膨胀的流程。

picture.image

锁的优缺点对比

对于偏向锁、轻量级锁和重量级锁这三者的优缺点,以及适用场景,我们可以通过下面的表格得到一个直观的了解:

picture.image

总结

最后,我们来总结一下所讲的主要内容。

首先,我们一起学习了 Synchronized 的底层实现,Synchronized 作为一个关键字以它极简的语法也带来了易读性;之后,我带你了解了偏向锁的初始化、撤销、关闭操作和轻量级锁的加锁、解锁过程;最后,我带你分析了不同锁的优缺点及适用场景,这些对你理解为什么 Synchronized 具备高性能是非常关键的。

此外,不同锁的,如偏向锁、轻量级锁对使用者来说是透明的,这也体现了 Synchronized 的简单性。

随着 JDK 的不断发展,Synchronized 已经做了足够多的性能优化。

Synchronized 从一个开销很大的重量级锁被优化成一个可自动适配场景的“智能”锁,它可以根据场景转换成偏向锁、轻量级锁,万不得已的情况下才会转换成重量级锁。

它的应用场景也随着这些特性逐渐丰富起来,在很多高并发场景甚至替代了 ReentrantLock。

最后欢迎

加入苏三的星球

,你将获得:AI开发项目课程、苏三AI项目、

商城微服务实战、秒杀系统实战

商城系统实战、秒杀系统实战、代码生成工具、系统设计、性能优化、技术选型、底层原理、Spring源码解读、工作经验分享、痛点问题

、面试八股文

等多个优质专栏。

还有1V1答疑、修改简历、职业规划、送书活动、技术交流。

扫描下方二维码,即可加入星球:

picture.image

目前星球已经更新了 5200+ 篇优质内容,还在持续爆肝中.....

星球已经被 官方推荐了3次 ,收到了小伙伴们的一致好评。戳我加入学习,已有 1600+ 小伙伴加入学习。

picture.image

苏三的免费八股文网站:

www.susan.net.cn

picture.image

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
如何利用云原生构建 AIGC 业务基石
AIGC即AI Generated Content,是指利用人工智能技术来生成内容,AIGC也被认为是继UGC、PGC之后的新型内容生产方式,AI绘画、AI写作等都属于AIGC的分支。而 AIGC 业务的部署也面临着异构资源管理、机器学习流程管理等问题,本次分享将和大家分享如何使用云原生技术构建 AIGC 业务。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论