我们来聊聊锁升级吧

本文探讨了Java中的锁升级过程,包括无锁到偏向锁、轻量级锁再到重量级锁的转换。文章指出,JDK 15废弃偏向锁主要是因为早期线程安全集合如Hashtable和Vector的使用减少,以及撤销偏向锁的高成本。作者解释了锁升级优化的场景,以及自适应锁的概念,自适应锁允许JVM根据自旋成功率来决定是否继续自旋或切换到阻塞状态。
摘要由CSDN通过智能技术生成

前言

在很久之前,我在面实习生的时候,就有人问过我synchronized的锁升级过程,我当时只是浅浅了解,后面其实了解了锁升级的流程。但其实我并不是很明白,究竟优化了哪里,究竟是针对哪种场景进行优化,我其实更想得到这个锁升级过程中的引入场景。尤其是看到JDK 15废弃并禁用了偏向锁之后,我其实在想为什么要移除这项技术,是JDK 有了更好的优化,还是这项技术不再适用于现在。这里直接说答案吧,答案就在JEP 374中。我本来想直接贴答案的,但是考虑到有的同学还不清楚synchronized的升级流程,这里还是先简单的讲一下锁升级的流程。

其实这也是一道面试常见的问题,但是常常是面试官问我锁升级的过程,而不会问哪些场景会从锁升级中受益,这也是我常常疑惑的地方,不去问why,而是问what。

总有些口口流传的优化,大家都愿意相信,但是我们都要相信那句话: 没有调查就没有发言权。

synchronized锁简介

这里我们简单的复习一下synchronized, synchronized是我们遇到的第一个同步工具,它有许多别名: 内部锁、排他锁、悲观锁。它能够保障原子性、可见性和有序性。synchronized 关键字修饰的方法就被称为同步方法(Synchronized Method), synchronized修饰的静态实例方法就被称为同步实例方法。同步方法的整个方法称为临界区。

// 修饰方法
public synchronized void synchronizedDemo(){

}   
// 修饰静态方法
public static  synchronized void synchronizedStaticDemo(){
                
}    
public void synchronizedDemoPlus(){
   // 修饰代码块
   synchronized (this){
            
   }
}

Java平台的任何一个对象都有唯一一个与之关联的锁。 线程进入临界区需要申请锁,那么锁放在哪里呢? 答案是对象头,一个普通的Java对象的内部构造如下图所示:

一般来说,我们对这个锁的认知是,多个线程进入临界区的时候,会申请获得这个锁,如果锁已经被其他线程所获取,那么这个线程会陷入阻塞状态。

更为准确的描述是JVM会为每个内部锁分配一个入口集,用于记录等待获得相应内部锁的线程,当这些线程申请的锁被其持有线程释放的时候,该锁入口集中的一个任意线程会被JVM唤醒。看到这里可能有同学会问,上面提到了获得锁和释放锁,JVM是怎么处理的呢。 这个其实要借助反编译指令,这里我们以同步代码块来观察synchronized的内部实现:

/**
 * 这个代码要编译一下,形成字节码,
 * 其实问题的答案就在字节码上。
 */
public class ThreadDemo{
  public  void synchronizedDemo(){
     synchronized (this){

      }
  }
}

然后找到这个类对应的字节码所在的文件夹,打开命令行执行如下指令

javap -c ./ThreadDemo.class

monitorenter 代表进入临界区和申请锁指令,monitorexit代表出临界区,释放锁指令。那为什么会有两个释放锁指令,这个问题问的好,最下面的那个释放锁指令是为临界区的代码出了异

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值