java八股文面试[多线程]——synchronized锁升级过程

这篇具有很好参考价值的文章主要介绍了java八股文面试[多线程]——synchronized锁升级过程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

java八股文面试[多线程]——synchronized锁升级过程,java八股文,java,面试,开发语言

速记:偏向-轻量-重量

锁膨胀

上面讲到锁有四种状态,并且会因实际情况进行膨胀升级,其膨胀方向是:无锁——>偏向锁——>轻量级锁——>重量级锁,并且膨胀方向不可逆

一.锁升级理论.
在synchronized锁升级过程中涉及到以下几种锁.先说一下这几种锁是什么意思.

偏向锁:只有一个线程争抢锁资源的时候.将线程拥有者标识为当前线程.
轻量级锁(自旋锁):一个或多个线程通过CAS去争抢锁,如果抢不到则一直自旋.
重量级锁:多个线程争抢锁,向内核申请锁资源,将未争抢成功的锁放到队列中直接阻塞.

为什么要有锁的升级过程?
      在最开始的时候,其实就是无锁直接到重量级锁,但是重量级锁需要向内核申请额外的锁资源,这就涉及到用户态和内核态的转换,比较浪费资源,而且大多数情况下,其实还是一个线程去争抢锁,完全不需要重量级锁.

锁的具体升级过程(通常情况下):

1.当只有一个线程去争抢锁的时候,会先使用偏向锁,就是给一个标识,说明现在这个锁被线程a占有.
2.后来又来了线程b,线程c,说凭什么你占有锁,需要公平的竞争,于是将标识去掉,也就是撤销偏向锁

3.升级为轻量级锁,三个线程通过CAS进行锁的争抢(其实这个抢锁过程还是偏向于原来的持有偏向锁的线程).现在线程a占有了锁,线程b,线程c一直在循环尝试获取锁,后来又来了十个线程,一直在自旋,那这样等着也是干耗费CPU资源,所以就将锁升级为重量级锁,向内核申请资源,直接将等待的线程进行阻塞.
锁升级的过程如下所示:

java八股文面试[多线程]——synchronized锁升级过程,java八股文,java,面试,开发语言

什么情况下偏向锁才会升级为轻量级锁,什么时候轻量级锁才会升级为重量级锁?

只有一个线程的时候就是偏向锁(当偏向锁开启的时候,偏向锁默认开启),当争抢的线程超过一个,升级为轻量级锁.
当自旋的线程循环超过10次,或者线程等待的数量超过cpu的1/2,升级为重量级锁.其实轻量级锁就适用于那种执行任务很短的线程,可能通过一两次自旋,就能够获取到锁.
开启偏向锁一定比轻量级锁高效吗?
      不一定,比如在一开始已经知道某个资源就需要被多个线程争抢,此时就不需要开启偏向锁,因为偏向锁给了标识之后,还需要取消这个标识,重新抢锁,比如在JVM中,偏向锁默认是延迟4秒才开始的,因为JVM在启动的时候需要多个线程竞争资源,并且这个都是一开始知道的.
 

对象在内存中的内存布局
在堆中,一个对象会包含以下四个部分:

 java八股文面试[多线程]——synchronized锁升级过程,java八股文,java,面试,开发语言

java八股文面试[多线程]——synchronized锁升级过程,java八股文,java,面试,开发语言

  1. 实例数据:存放类的属性数据信息,包括父类的属性信息;
  2. 对齐填充:由于虚拟机要求 对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐;
  3. 对象头:Java对象头一般占有2个机器码(在32位虚拟机中,1个机器码等于4字节,也就是32bit,在64位虚拟机中,1个机器码是8个字节,也就是64bit),但是 如果对象是数组类型,则需要3个机器码,因为JVM虚拟机可以通过Java对象的元数据信息确定Java对象的大小,但是无法从数组的元数据来确认数组的大小,所以用一块来记录数组长度

Synchronized用的锁就是存在Java对象头里的

java八股文面试[多线程]——synchronized锁升级过程,java八股文,java,面试,开发语言

我们如果使用synchronized对某个对象进行加锁,就会体现在mark word区域.最低两个字节加以标识.
如下如所示:

下图是Java对象头 无锁状态下Mark Word部分的存储结构(32位虚拟机): 25+4+1+2=32

java八股文面试[多线程]——synchronized锁升级过程,java八股文,java,面试,开发语言

Mark Word存储结构 

对象头信息是与对象自身定义的数据无关的额外存储成本,但是考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据,它会根据对象的状态复用自己的存储空间,也就是说,Mark Word会随着程序的运行发生变化,可能变化为存储以下4种数据:

java八股文面试[多线程]——synchronized锁升级过程,java八股文,java,面试,开发语言

轻量级锁 对应 00

重量级锁 对应 10

无锁和偏向锁都对应01,这个时候需要倒数第三个字节加以区分,即 无锁 对应 001, 偏向锁 对应 101

Mark Word可能存储4种数据

在64位虚拟机下,Mark Word是64bit大小的,其存储结构如下:

java八股文面试[多线程]——synchronized锁升级过程,java八股文,java,面试,开发语言

64位Mark Word存储结构

对象头的最后两位存储了锁的标志位,01是初始状态,未加锁,其对象头里存储的是对象本身的哈希码,随着锁级别的不同,对象头里会存储不同的内容。偏向锁存储的是当前占用此对象的线程ID;而轻量级则存储指向线程栈中锁记录的指针。从这里我们可以看到,“锁”这个东西,可能是个锁记录+对象头里的引用指针(判断线程是否拥有锁时将线程的锁记录地址和对象头里的指针地址比较),也可能是对象头里的线程ID(判断线程是否拥有锁时将线程的ID和对象头里存储的线程ID比较)。 

java八股文面试[多线程]——synchronized锁升级过程,java八股文,java,面试,开发语言

对象头中Mark Word与线程中Lock Record

在线程进入同步代码块的时候,如果此同步对象没有被锁定,即它的锁标志位是01,则虚拟机首先在当前线程的栈中创建我们称之为“锁记录(Lock Record)”的空间,用于存储锁对象的Mark Word的拷贝,官方把这个拷贝称为Displaced Mark Word。整个Mark Word及其拷贝至关重要。

Lock Record是线程私有的数据结构每一个线程都有一个可用Lock Record列表,同时还有一个全局可用列表。每一个被锁住的对象Mark Word都会和一个Lock Record关联(对象头的MarkWord中的Lock Word指向Lock Record的起始地址),同时Lock Record中有一个Owner字段存放拥有该锁的线程的唯一标识(或者object mark word),表示该锁被这个线程占用。如下图所示为Lock Record的内部结构

Lock Record 描述
Owner 初始时为NULL表示当前没有任何线程拥有该monitor record,当线程成功拥有该锁后保存线程唯一标识,当锁被释放时又设置为NULL;
EntryQ 关联一个系统互斥锁(semaphore),阻塞所有试图锁住monitor record失败的线程;
RcThis 表示blocked或waiting在该monitor record上的所有线程的个数;
Nest 用来实现 重入锁的计数;
HashCode 保存从对象头拷贝过来的HashCode值(可能还包含GC age)。
Candidate 用来避免不必要的阻塞或等待线程唤醒,因为每一次只有一个线程能够成功拥有锁,如果每次前一个释放锁的线程唤醒所有正在阻塞或等待的线程,会引起不必要的上下文切换(从阻塞到就绪然后因为竞争锁失败又被阻塞)从而导致性能严重下降。Candidate只有两种可能的值0表示没有需要唤醒的线程1表示要唤醒一个继任线程来竞争锁。

java八股文面试[多线程]——synchronized锁升级过程,java八股文,java,面试,开发语言

其他注意点:
      并不是一定会有一个偏向锁->轻量级锁->重量级锁的过程,比如如果出现严重的耗时操作(sleep,或者wait等),就会直接由偏向锁升级为重量级锁.

知识来源:

【并发与线程】Sychronized的偏向锁、轻量级锁、重量级锁_哔哩哔哩_bilibili

【2023年多线程面试】无锁、偏向锁、轻量级锁、重量级锁升级过程(多线程面试)_哔哩哔哩_bilibili

 synchronized锁升级过程_程序员bling的博客-CSDN博客

https://www.cnblogs.com/aspirant/p/11470858.html文章来源地址https://www.toymoban.com/news/detail-683940.html

到了这里,关于java八股文面试[多线程]——synchronized锁升级过程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java八股文面试[多线程]——线程池拒绝策略

    四种线程池拒绝策略(handler)           当线程池的线程数达到最大线程数时,需要执行拒绝策略。拒绝策略需要实现 RejectedExecutionHandler 接口,并实现 rejectedExecution (Runnable r, ThreadPoolExecutor executor) 方法。不过 Executors 框架已经为我们实现了 4 种拒绝策略: Abort Policy(默认

    2024年02月10日
    浏览(47)
  • java八股文面试[多线程]——自旋锁

    优点: 1.  自旋锁尽可能的减少线程的阻塞, 这对于锁的竞争不激烈,且占用锁时间非常短的代码块来说性能能大幅度的提升,因为自旋的消耗会小于线程阻塞挂起再唤醒的操作的消耗  ,这些操作会导致线程发生两次上下文切换! 2. 非自旋锁在获取不到锁的时候会进入阻

    2024年02月10日
    浏览(51)
  • java八股文面试[多线程]——并发三大特性 原子 可见 顺序

        AutomicInteger :  volatile + CAS 总线LOCK  MESI 两个协议 TODO volatile的可见性和禁止重排序是怎么实现的: DCL场景:  new操作会在字节码层面生成两个步骤: 分配内存、调用构造器 然后把引用赋值给singleton 不加volatile则会发生指令重排,可能得到不完整的对象 知识来源: 【并

    2024年02月11日
    浏览(53)
  • java八股文面试[多线程]——两个线程交替打印1-100之间的数字

    一份代码,两个线程,使用synchronize实现: 重写run()方法,将输出1到100之间整数的代码写到同步方法里。 线程1进入到同步方法,输出一个整数后,阻塞并释放锁。 线程2进入到同步方法,唤醒线程1,输出整数后,阻塞并释放锁。 线程1和线程2重复第3步,直到输出所有的整数

    2024年02月11日
    浏览(45)
  • java八股文面试[多线程]——主内存和工作内存的关系

    JAVA内存模型(JMM) 共享变量 :如果一个变量在多个线程的工作内存中 都存在副本 ,那么这个变量就是这几个线程的共享变量。 上面的工作内存其实是java内存模型 抽象出来的概念 ,下面简要介绍一下java内存模型(JMM)。 java内存模型( java memory model ): 描述了java程序中各

    2024年02月10日
    浏览(47)
  • java八股文面试[多线程]——ThreadLocal底层原理和使用场景

    源码分析: ThreadLocal中定义了ThreadLocalMap静态内部类,该内部类中又定义了Entry内部类。 ThreadLocalMap定了 Entry数组。 Set方法: Get方法: Thread中定义了两个ThreaLocalMap成员变量: Spring使用ThreadLocal解决线程安全问题  我们知道在一般情况下,只有 无状态的Bean 才可以在多线程环

    2024年02月10日
    浏览(52)
  • java八股文面试[多线程]——为什么要用线程池、线程池参数

     速记7个: 核心、最大 存活2 队列 工厂 拒绝 线程池处理流程: 线程池底层工作原理: 线程复用原理:   知识来源: 【并发与线程】为什么使用线程池,参数解释_哔哩哔哩_bilibili 【并发与线程】线程池处理流程_哔哩哔哩_bilibili 【并发与线程】线程池的底层工作原理_哔哩

    2024年02月11日
    浏览(51)
  • java八股文面试[多线程]——sleep wait join yield

          sleep和wait有什么区别 sleep 方法和 wait 方法都是用来将线程进入 阻塞状态 的,并且 sleep 和 wait 方法都可以响应 interrupt 中断,也就是线程在休眠的过程中,如果收到中断信号,都可以进行响应并中断,且都可以抛出 InterruptedException 异常,那 sleep 和 wait 有什么区别呢?

    2024年02月11日
    浏览(45)
  • 【面试系列】八股文之线程篇202306

    union all :包含重复行 union :不包含重复行 shutdown() ,调用shutdown方法,线程池会拒绝接收新的任务,处理中的任务和阻塞队列中的任务会继续处理。 shutdownNow() ,会给workers中所有的线程发送 interrupt 信号,将延迟队列的任务移除并返回。 原理分析 执行任务,尝试添加线程。

    2024年02月12日
    浏览(47)
  • 【面试八股文】每日一题:谈谈你对线程的理解

    每日一题-Java核心-谈谈你对线程的理解【面试八股文】   Java线程是Java程序中的执行单元。一个Java程序可以同时运行多个线程,每个线程可以独立执行不同的任务。线程的执行是并发的,即多个线程可以同时执行。   Java中的线程有如下的特点 轻量级:线程的创建和销毁

    2024年02月12日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包