面试官:重量级锁的8连问,你能接住几个?






面试官:偏向锁的十连问,你能接住几个?

正文:

接上一篇偏向锁的十连问,继续升级到重量级锁的进阶版,检验一下自己离精通重量级锁还有多远。建议在读之前了解下Java中重量级锁的实现原理。

  1. 重量级锁的ObjectMonitor和JUC中的AQS有什么异同

  2. 为什么ObjectMonitor需要cxq和entryList两个等待队列

  3. cxq队列中等待线程,什么时候会进到EntryList

  4. 等待队列中多个线程,唤醒的顺序是什么

  5. 偏向锁和轻量级锁下线程是否可以wait和notify

  6. cxq和waitset数据结构有什么区别

  7. 被唤醒的wait线程和其它等待线程,谁会先抢到锁

  8. synchronized有类似AQS的公平锁/非公平锁逻辑吗

看了上面的问题,如果是胸有成竹,那就可以跳过这篇文章了。如果一脸问号,这篇文章应该对你有所帮助。

名词解释

首先明确下文章中用到的名词,防止引起误解。

等待队列,互斥锁实现中,当线程抢锁失败时,会被放入一个队列等待。当别的线程释放锁后会唤醒队列中的元素重新尝试抢锁,这个队列一般称为互斥等待队列,本文中称为等待队列。

同步队列,代码中调用wait方法时,当前线程会放入另外一个队列,等待其它线程notify,这个队列一般称为同步等待队列,本文中称为同步队列。

问题解析

问题1:ObjectMonitor和AQS有什么异同

ObjectMonitor和AQS(AbstractQueuedSynchronizer)都是依据管程模型的原理开发的。所以在整体架构上基本相同,都有共享变量和等待队列,在实现上又有区别。

1)共享变量,ObjectMonitor中使用owner做共享变量,通过CAS设置owner为当前线程来抢锁。而AQS中的共享变量是一个整形的status。因为这一区别,导致ObjectMonitor需要定义一个计数器来记录锁重入次数,而AQS需要额外定义个exclusiveOwnerThread来记录当前持有锁的线程。

2)等待队列,ObjectMonitor等待队列使用了两个队列,cxq和entryList,而AQS仅使用了一个等待队列。

3)条件同步,AQS支持在同一个锁上创建多个条件变量,wait/notify更加灵活和精准。而ObjectMonitor只有一个waitset,所有线程共享一个条件变量。

4)Share模式,AQS的Share模式可以使实现读写锁更加简单。

问题2: 为什么ObjectMonitor需要cxq和entryList两个等待队列

ObjectMonitor中加解锁、wait/notify都涉及对等待队列的进出队操作。如果使用一个队列冲突的概率会加大,耗费系统资源。分成2个队列后,出入队EntryList队列只有加锁的情况才会操作,不需要CAS和自旋,减少了资源消耗。

问题3:cxq队列中等待线程,什么时候会进到EntryList

抢锁线程在获取锁失败后,默认会进cxq队列。当持有锁的线程执行完释放锁时,会将cxq中的等待节点放入EntryList中。就是说cxq->EntryList这一步是锁释放之前的由持有锁的线程做的。

问题4:等待队列中多个线程,唤醒的顺序是什么

当持有锁的线程释放锁时,会先检查EntryList是否为空,如果不为空则唤醒EntryList中第一个节点。否则唤醒cxq中第一个节点。EntryList和cxq中出入队策略请看问题6。

问题5:偏向锁和轻量级锁下线程是否可以wait和notify

答案时是可以。原因很简单,因为wait/notify时是需要加入或者唤醒同步队列的,只有ObjectMonitor中才有同步队列。

问题6:cxq和waitset数据结构有什么区别

  1. cxq是一个双向链表,采用先进后出的策略,就是说后入队的线程将先获取到互斥锁,结构如下图:

    当前锁被其它线程持有,t0先尝试获取锁,t3最后尝试,cxq当前的状态如上图。最后入队的t3会排在第一位。当持有锁的线程解锁时,正常从队首出队,所以t3首先获得锁。另外,搜索公众号编程技术圈后台回复“大礼包”,获取一份惊喜礼包。

  2. waiset是一个回环链表,即尾节点的下一个节点是头节点,采用先进先出的策略。结构如下图:


问题7:notify/notifyAll后的线程和等待队列中线程,谁会优先抢到锁

使用notify和notifyAll唤醒wait线程,jvm的处理是有区别的。

1)如果是notify,唤醒的是waitset的队首节点,如果这时候EntryList不为空,则放入EntryList,否则放入cxq。无论是放入那个队列。因为是cxq后进先出,所以被唤醒的线程比等待队列中的线程先出队,会先抢到锁。

2)如果是notifyAll,会将waitset中的所有节点逐个放入cxq中。按照问题4中的描述,如果EntryList不为空,则EntryList中首节点会先抢到锁,否则waitset中原最后一个节点先抢到锁,如下图所示:

问题8:Synchronized有类似AQS的公平锁/非公平锁逻辑吗

默认情况下,线程进入重量级锁的抢锁阶段,第一步就会尝试通过自旋来抢锁,所以默认相当于AQS中的非公平锁。即使自旋时未抢到锁,按照上面讲的cxq出入队逻辑,也是后进先出,正常情况下后进入等待队列的线程会先抢到锁,这一点也是和AQS中相反的。

hotspot中对于重量级锁的不同使用场景可以调整这个公平锁逻辑,但是不提供jvm启动参数,需要修改jvm的编译参数来实现。


总结

JVM中的Synchronized重量级锁逻辑和JDK中的AQS都是依据管程模型的理论来设计的,所以有诸多的相似之处。建议感兴趣的读者可以了解下管程模型,对于理解互斥锁会有很大帮助的。







END

1.说说悲观锁、乐观锁、分布式锁?都在什么场景下使用?有什么技巧?2.什么是自旋锁?自旋的好处和后果是什么呢?3.SpringBoot中使用自定义注解来实现 Redis 分布式锁4.死锁问题排查过程-表锁、行锁、间隙锁?

我是技术管家,专心做内容,不割韭菜

分享技术成长之路,不忘初心,惠泽他人终身学习,与时俱进,点赞关注不迷路

相关推荐

  • 面试官:SpringBoot 如何自定义自己的条件注解与自动配置?
  • 五一回老婆家,大家聊工资的时候,我说自己月薪3w算个中产吧!结果岳父说你算啥中产?你看看你哥,两口子是公务员,那才是真正的中产!
  • 这是我见过最好的轻量级笔记系统!
  • 15年前被钉在“FFmpeg耻辱柱”,今天他却得谢谢咱——腾讯QQ影音一雪前耻?
  • 斯坦福20亿参数端测多模态AI Agent模型大升级,手机汽车机器人都能用
  • 苹果深夜扔出M4核弹,iPad Pro碾压所有AI PC!280亿晶体管3nm工艺称霸地表
  • [开源]新一代代码生成器,像Jenkins打包一样生成代码,协作方便
  • 为啥我说英语能决定程序员的天花板?
  • 阿里面试:写一个倒计时功能刷掉了80% 的人
  • 小马宋:公司一定没有人情味吗?
  • 【小象AI第9讲】自然语言处理NLP入门:词向量
  • 百度副总裁短视频翻车,内部员工怎么说?
  • 每日prompt:换一种画风的龙珠人物
  • OpenAI 挑战谷歌主导地位推基于人工智能的搜索产品,Stability AI推Stable Artisan产品
  • 如何确定神经网络的层数和隐藏层神经元数量?
  • 基于深度学习的直线检测算法
  • 四月及五一假期LLM+KG+RAG产研总结:开源的继续奔放与RAG等的转向
  • 60个“特征工程”计算函数(Python代码)
  • 被严重低估!React 19 又是一次开发方式的变革,useEffect 将逐渐退出历史舞台
  • 鹅厂开招天才高中生!专门挑战产业难题,张胜誉于旸教主领衔带队