面试~Synchronized 与 锁升级

简介: 面试~Synchronized 与 锁升级

讲讲 Synchronized/ 讲讲 Synchronized 锁升级

内部实现 Markword

synchronized在修饰方法和代码块在字节码上实现方式有很大差异,但是内部实现还是基于对象头的MarkWord来实现的


jdk5 以前 ---重量级锁

synchronized 只有重量级锁,Synchronized是通过对象内部的一个叫做 监视器锁 (Monitor)来实现的。

但是 监视器锁本质又是依赖于底层的操作系统的 互斥锁 Mutex Lock来实现的。

并且 操作系统实现线程之间的切换这就需要从用户态转换到内核态,这个成本非常高

状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低`的原因。

因此,这种依赖于操作系统的互斥锁 Mutex Lock实现的锁我们称之为 "重量级锁"。


JDK6 开始 ---偏向锁、轻量锁

对synchronized 进行优化,增加了自适应的 CAS自旋、锁消除、锁膨胀、偏向锁轻量级锁 这些优化策略。

锁可以从偏向锁升级到轻量级锁,再升级的重量级锁。但是锁的升级是单向的,也就是说只能从低到高升级

在 JDK 1.6 中默认是开启偏向锁和轻量级锁的,可以通过-XX:-UseBiasedLocking来禁 用偏向锁。


偏向锁

偏向锁的引进,因为HotSpot作者经过研究实践发现,

在大多数的情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低,引进了偏向锁。

在线程持有偏向锁之后,后续对同步代码块的访问都不需要获取锁、释放锁。

偏向锁是在单线程执行代码块时使用的机制,如果在多线程并发的环境下(即线程A 尚未执行完同步代码块,线程B发起了申请锁的申请),则一定会转化为轻量级锁或者重量级锁

"偏向"的意思是,偏向锁假定将来只有第一个申请锁的线程会使用锁(不会有任何线程再来申请锁)

因此,只需要在Mark Word中CAS记录owner(本质上也是更新,但初始值为空),

如果记录成功,则偏向锁获取成功,记录锁状态为偏向锁,以后当前线程等于owner就可以零成本的直接获得锁;

否则,说明有其他线程竞争,膨胀为轻量级锁。


轻量锁

引入轻量级锁的目的其实是为了避免使用重量级锁。

通过CAS 自旋,避免了短时间内对线程进行阻塞、唤醒,因为线程的阻塞、唤醒对应着操作系统的用户态和内核态的切换,从而节省资源,提高程序运行的性能。


所以说,各种锁并不是相互代替的,而是在不同场景下的不同选择

  • 偏向锁:无实际竞争,且将来只有第一个申请锁的线程会使用锁。
  • 轻量级锁:无实际竞争,多个线程交替使用锁;允许短时间的锁竞争。
  • 重量级锁:有实际竞争,且激烈竞争

synchronized锁升级过程总结:一句话,就是先自旋,不行再阻塞。



其他必备知识

CAS 自旋

// 执行一个无意义的循环,目的就是等代机会去竞争到锁
while(true){
  //空的
  //一个:每次自旋的时间
  //另外一个:自旋的次数
}

自旋的作用:

避免了短时间内对线程进行阻塞、唤醒,因为线程的阻塞、唤醒对应着操作系统的用户态和内核态的切换,从而节省资源,提高程序运行的性能。


锁信息存放

锁的信息是存放在对象头的 Mark word 中,Mark word 根据锁的状态,存储对应的锁的信息

synchronized 与 锁升级:由对象头中的 Mark Word 根据 锁标志位 的不同而被复用及锁升级策略。

  • 偏向锁:MarkWord 存储的是 偏向的线程ID
  • 轻量锁:MarkWord 存储的是 指向线程中Lock Record的指针
  • 重量锁:MarkWord 存储的是 指向 中的 monitor对象的指针


锁的状态

无锁状态

偏向锁状态

轻量级锁状态

重量级锁状态


各种锁的优点、缺点


阿里巴巴开发手册

【强制】高并发时,同步调用应该去考量 锁的性能损耗

能用无锁数据结构,就不要用锁能锁区块,就不要锁整个方法体能用对象锁,就不要用类锁

说明:尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用RPC方法。


常见面试题

1、你提到了synchronized 的优化,详细说一下 偏向锁、轻量级锁有什么区别?

CAS次数不同、是否主动释放锁

轻量级锁每次申请、释放锁都至少需要一次CAS,而偏向锁只有初始化时需要一次CAS.


偏向锁,只偏向于第一个访问的线程。在这个线程(线程A)第一次来访问同步块时,会使用CAS,更新对象头的ThreadID为偏向线程的id。后续的访问,只需要比较threadId是否相同,不需要CAS操作。且这个偏向的线程是不会主动十分锁的,除非出现线程来竞争。

当第二个线程(线程B)过来访问时,他并不知第一个线程已经存在。所以这第二个线程以为它是被偏爱的,它也想向偏向线程那样使用CAS初始化对象头的ThreadID,发现失败了。

说明对象锁已经被其他线程占用,出现线程竞争

检查原来持有该对象锁的线程是否依然存活

如果挂了,则可以将对象变为无锁状态,然后重新偏向新的线程。

如果线程还存活,则检查线程是否在执行同步代码块中的代码,如果是,则升级为轻量级锁,进行CAS竞争锁。

轻量级锁的CAS 是每次申请锁都需要执行的


2、你平时是怎么使用 synchronized 关键字

(1) 修饰实例方法: 给当前对象实例加锁

synchronized void method() {
 //业务代码
}

(2) 修饰静态⽅法: 也就是给当前类加锁

synchronized void staic method() {
 //业务代码
}

(3) 修饰代码块

synchronized(this|object|xx.class) {
 //业务代码
}

单例模式的双重检索也是使用单例

/* 双重检验锁  */
public class Singleton{
 private Singleton(){}//构造器私有化,防止new,导致多个实例
 private static volatile Singleton singleton;
 public static Singleton getInstance(){//向外暴露一个静态的公共方法  getInstance
     //第一层检查
     if(singleton == null){
         //同步代码块
         synchronized (Singleton.class){
              //第二层检查
             if(singleton == null) {
                 singleton = new Singleton();
             }
         }
     }
     return singleton;
 }
}



如果本文对你有帮助的话记得给一乐点个赞哦,感谢!

目录
相关文章
|
1月前
|
存储 安全 Java
面试高频:Synchronized 原理,建议收藏备用 !
本文详解Synchronized原理,包括其作用、使用方式、底层实现及锁升级机制。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
面试高频:Synchronized 原理,建议收藏备用 !
|
2月前
|
NoSQL Java API
美团面试:Redis锁如何续期?Redis锁超时,任务没完怎么办?
在40岁老架构师尼恩的读者交流群中,近期有小伙伴在面试一线互联网企业时遇到了关于Redis分布式锁过期及自动续期的问题。尼恩对此进行了系统化的梳理,介绍了两种核心解决方案:一是通过增加版本号实现乐观锁,二是利用watch dog自动续期机制。后者通过后台线程定期检查锁的状态并在必要时延长锁的过期时间,确保锁不会因超时而意外释放。尼恩还分享了详细的代码实现和原理分析,帮助读者深入理解并掌握这些技术点,以便在面试中自信应对相关问题。更多技术细节和面试准备资料可在尼恩的技术文章和《尼恩Java面试宝典》中获取。
美团面试:Redis锁如何续期?Redis锁超时,任务没完怎么办?
|
3月前
|
消息中间件 安全 前端开发
面试官:单核服务器可以不加锁吗?
面试官:单核服务器可以不加锁吗?
53 4
面试官:单核服务器可以不加锁吗?
|
3月前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
2月前
|
存储 Kubernetes 架构师
阿里面试:JVM 锁内存 是怎么变化的? JVM 锁的膨胀过程 ?
尼恩,一位经验丰富的40岁老架构师,通过其读者交流群分享了一系列关于JVM锁的深度解析,包括偏向锁、轻量级锁、自旋锁和重量级锁的概念、内存结构变化及锁膨胀流程。这些内容不仅帮助群内的小伙伴们顺利通过了多家一线互联网企业的面试,还整理成了《尼恩Java面试宝典》等技术资料,助力更多开发者提升技术水平,实现职业逆袭。尼恩强调,掌握这些核心知识点不仅能提高面试成功率,还能在实际工作中更好地应对高并发场景下的性能优化问题。
|
2月前
|
存储 算法 安全
HashMap常见面试题(超全面):实现原理、扩容机制、链表何时升级为红黑树、死循环
HashMap常见面试题:红黑树、散列表,HashMap实现原理、扩容机制,HashMap的jd1.7与jdk1.8有什么区别,寻址算法、链表何时升级为红黑树、死循环
|
2月前
|
存储 安全 Java
面试题:再谈Synchronized实现原理!
面试题:再谈Synchronized实现原理!
|
4月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
1月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
1月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
下一篇
DataWorks