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

2023-12-19


前言

接上一篇偏向锁的十连问,继续升级到重量级锁的进阶版,检验一下自己离精通重量级锁还有多远。建议在读之前了解下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首先获得锁。另外,搜索公众号编程技术圈后台回复“大礼包”,获取一份惊喜礼包。

  1. 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都是依据管程模型的理论来设计的,所以有诸多的相似之处。建议感兴趣的读者可以了解下管程模型,对于理解互斥锁会有很大帮助的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

面试题:重量级锁的8连问,你能接住几个? 的相关文章

  • Java:扩展类并实现具有相同方法的接口

    可能无法完成以下操作 我收到编译错误 继承的方法 A doSomthing int 无法隐藏 B 中的公共抽象方法 public class A int doSomthing int x return x public interface
  • 如何在由子控件组成的 SWT 复合材料上跟踪鼠标?

    我创建了自己的控件 我想跟踪鼠标并添加一个MouseTrackListener 很遗憾MouseEnter and MouseLeave当鼠标移动到我的合成部分 即标签和按钮 上时 也会生成事件 Mouse enter mouse ente
  • 如何在java中将数组值排序为循环格式?

    我的数组值如下 String value 1 2 3 4 5 6 7 8 9 10 假设如果我将值 5 传递给 tat 数组 它应该按如下顺序排序 5 6 7 8 9 10 1 2 3 4 怎么办 有人帮忙吗 感谢你 你需要的就是所谓的轮换
  • 如何在 JavaFX 中连接可观察列表?

    我所说的串联是指获得一个新列表 该列表侦听所有串联部分的更改 方法的目的是什么FXCollections concat ObservableList
  • 两个整数乘积的模

    我必须找到c c a b mod m a b c m 是 32 位整数 但 a b 可以超过 32 位 我正在尝试找出一种计算 c 的方法 而不使用 long 或任何 gt 32 位的数据类型 有任何想法吗 如果m是质数 事情可以简化吗 注
  • Android:文本淡入和淡出

    我已阅读此 stackoverflow 问题和答案 并尝试实现文本淡入和淡出 Android中如何让文字淡入淡出 https stackoverflow com questions 8627211 how to make text fade
  • 从 MATLAB 调用 Java?

    我想要Matlab程序调用java文件 最好有一个例子 需要考虑三种情况 Java 内置库 也就是说 任何描述的here http docs oracle com javase 6 docs api 这些项目可以直接调用 例如 map ja
  • 提供节点名或服务名,或未知 Java

    最近我尝试运行我的 Java 项目 每当我运行它并将其打开到我得到的服务器地址时 Unable to determine host name java net UnknownHostException Caused by java net
  • Mockito 使用 @Mock 时将 Null 值注入到 Spring bean 中?

    由于我是 Spring Test MVC 的新手 我不明白这个问题 我从以下代码中获取了http markchensblog blogspot in search label Spring http markchensblog blogsp
  • 如何在 Spring 中使 @PropertyResource 优先于任何其他 application.properties ?

    我正在尝试在类路径之外添加外部配置属性资源 它应该覆盖任何现有的属性 但以下方法不起作用 SpringBootApplication PropertySource d app properties public class MyClass
  • Android 无法解析日期异常

    当尝试解析发送到我的 Android 客户端的日期字符串时 我得到一个无法解析的日期 这是例外 java text ParseException 无法解析的日期 2018 09 18T00 00 00Z 位于 偏移量 19 在 java t
  • Sun 在 EDT 之外做 GUI 工作的演示?

    我正在看SplashDemo java http download oracle com javase tutorial uiswing examples misc SplashDemoProject src misc SplashDemo
  • 提高 PostgreSQL 1 亿数据左连接查询性能

    我在用Postgresql 9 2 version Windows 7 64 bit RAM 6GB 这是一个Java企业项目 我必须在我的页面中显示订单相关信息 有三个表通过左连接连接在一起 Tables TV HD 389772 行 T
  • 轻松的反应

    我有一个与这里描述的类似的案例 动态更改RESTEasy服务返回类型 https stackoverflow com questions 3786781 dynamically change resteasy service return
  • hibernate 6.0.2.Final 和 spring boot 2.7.0 的entityManagerFactory bean 未配置问题

    所以最近我想升级我的 Spring Boot 项目项目的一些依赖项 特别是这些组件 雅加达 EE 9 弹簧靴2 7 休眠 6 0 2 Final 完成此操作后 所有更新和代码折射 更新将 javax 导入到 jakarta 以及一些 hib
  • 将 JavaFX FXML 对象分组在一起

    非常具有描述性和信息性的答案将从我这里获得价值 50 声望的赏金 我正在 JavaFX 中开发一个应用程序 对于视图 我使用 FXML
  • 使用 HtmlUnit 定位弹出窗口

    我正在构建一个登录网站并抓取一些数据的程序 登录表单是一个弹出窗口 所以我需要访问这个www betexplorer com网站 在页面的右上角有一个登录链接 写着 登录 我单击该链接 然后出现登录弹出表单 我能够找到顶部的登录链接 但找不
  • 在java中以原子方式获取多个锁

    我有以下代码 注意 为了可读性 我尽可能简化了代码 如果我忘记了任何关键部分 请告诉我 public class User private Relations relations public User relations new Rela
  • partitioningBy 必须生成一个包含 true 和 false 条目的映射吗?

    The 分区依据 https docs oracle com javase 8 docs api java util stream Collectors html partitioningBy java util function Pred
  • Java 和/C++ 在多线程方面的差异

    我读过一些提示 多线程实现很大程度上取决于您正在使用的目标操作系统 操作系统最终提供了多线程能力 比如Linux有POSIX标准实现 而windows32有另一种方式 但我想知道编程语言水平的主要不同 C似乎为同步提供了更多选择 例如互斥锁

随机推荐