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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/882559.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

THP4 SOP16 芯片 高速光耦芯片

光电耦合器输入端加电信号使发光源发光,光的强度取决于激励电流的大小,此光照射到封装在一起的受光器上后,因光电效应而产生了光电流,由受光器输出端引出,这样就实现了电一光一电的转换。 由于光耦合器输入输出间互相…

5g工业路由器最新案例:高原气象站网络升级项目

背景: 某省气象局决定在高原地区升级其气象观测网络,以提高天气预报的准确性和及时性,同时为气候变化研究提供更可靠的数据支持。该项目面临以下挑战: 需要在高原广袤且地形复杂的区域部署大量自动气象站,要求网络覆…

Java配置 Redis 连接互斥锁或队列预先加载缓存

学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……) 2、学会Oracle数据库入门到入土用法(创作中……) 3、手把手教你开发炫酷的vbs脚本制作(完善中……) 4、牛逼哄哄的 IDEA编程利器技巧(编写中……) 5、面经吐血整理的 面试技…

【HuggingFace 如何上传数据集 (2) 】国内网络-稳定上传图片、文本等各种格式的数据

【HuggingFace 下载】diffusers 中的特定模型下载,access token 使用方法总结【HuggingFace 下载中断】Git LFS 如何下载指定文件、单个文件夹?【HuggingFace 如何上传数据集】快速上传图片、文本等各种格式的数据 上文的方法因为是 https 协议&#xf…

1024-过去一年的总结

目录 前言 (10月-24.01)故事的开始 (2月-7月)漫长的学习与探索 (8月-现在)低谷期 自我审视 前言 1024在即,就借此机会讲一讲过去一年的经历,也算是全方位总结一下自己 (10月-24.01)故事的开始 那就从去年大一入学…

【C++】STL——priority_queue优先级队列

目录 前言priority_queue的使用简单使用在OJ中的使用 priority_queue的模拟实现基本功能仿函数在这里插入图片描述 前言 上一节我们说了stack和queue这两种容器适配器,而priority_queue(优先级队列)同样也是属于容器适配器,它会优…

【Fargo】9:模拟图片采集的内存泄漏std::bad_alloc

std::bad_alloc 崩溃。这样的内存分配会导致内存耗尽 is simulating an image of size 640x480 with 3 bytes per pixel, resulting in an allocation of approximately 921,600 bytes (or around 900 KB) for each image. The error you’re encountering (std::bad_alloc) ty…

Java 多线程(四)—— 线程安全 与 volatile 与 单例模式

什么是线程安全 在进行多线程编程的时候,当我们编写出来的多线程的代码运行结果不符合我们的预期的时候,这时候就是 bug,这种 bug 是由于多线程的问题而产生出来的 bug 我们称之为 线程安全问题 当我们编写出来的多线程代码运行之后的结果符…

学习文档(5)

Redis应用 目录 Redis应用 Redis 除了做缓存,还能做什么? Redis 可以做消息队列么? Redis 可以做搜索引擎么? 如何基于 Redis 实现延时任务? Redis 除了做缓存,还能做什么? 分布式锁&…

三周精通FastAPI:5 查询参数和字符串校验

FastAPI手册:https://fastapi.tiangolo.com/zh/tutorial/query-params-str-validations/ 查询参数和字符串校验 FastAPI 允许你为参数声明额外的信息和校验。让我们以下面的应用程序为例: from fastapi import FastAPIapp FastAPI()app.get("/it…

基于springboot+thymeleaf+springsecurity搭建一套web小案例

一、前言 本案例中的源代码已上传到资源库,可自行下载,传送阵 https://download.csdn.net/download/qq_36260963/89906196 Spring Boot是为了简化Spring应用的创建、运行、调试、部署等而出现的,使用它可以做到专注于Spring应用的开发&#x…

git clone 鉴权失败

git clone 鉴权失败问题 1. 问题描述2. 解决方法 1. 问题描述 使用git clone自己的代码报如下错误: 正克隆到 xxx... Username for https://github.com: Password for https://xxxgithub.com: remote: Support for password authentication was removed on Augu…

RAG流程的实现与改进

一、 RAG流程图 数据入库:读取本地数据并切成小块,并把这些小块经过编码embedding后,存储在一个向量数据库中(下图1——6步);相关性检索:用户提出问题,问题经过编码,再在…

Vue项目中实现拖拽上传附件:原生JS与Element UI组件方法对比

在现代化的Web应用中,文件上传是一个基本功能。随着技术的发展,拖拽上传已经成为提升用户体验的一个重要特性。在Vue项目中,我们可以通过原生JavaScript或使用Element UI组件来实现这一功能。下面我们将分别介绍这两种方法,并对比…

吴恩达深度学习笔记(6)

正交化 为了提高算法准确率,我们想到的方法 收集更多的训练数据增强样本多样性使用梯度下降将算法使算法训练时间更长换一种优化算法更复杂或者更简单的神经网络利用dropout 或者L2正则化改变网络框架更换激活函数改变隐藏单元个数 为了使有监督机制的学习系统良…

vue使用jquery的ajax,页面跳转

一、引入jquery依赖 打开终端更新npm npm install -g npm 更新完后引入输入npm install jquery 加载完后 在最外层的package.json文件中加入以下代码 配置好后导入jquery 设置变量用于接收服务器传输的数据 定义ajax申请数据 服务器的Controller层传输数据 (…

【VUE小型网站开发】初始环境搭建

1. 初始化VUE项目 1.1 创建vue项目 1.2 删除多余的界面 根据自己情况删除红框内的文件 清理app页面代码 1.3 引入vue-router 1.3.1 下载vue-router npm install vue-router1.3.2 配置vue-router 在 main.js 或 main.ts 中引入 vue-router import ./assets/main.css im…

Android 图片相识度比较(pHash)

概述 在 Android 中,要比对两张 Bitmap 图片的相似度,常见的方法有基于像素差异、直方图比较、或者使用一些更高级的算法如 SSIM(结构相似性)和感知哈希(pHash)。 1. 基于像素的差异比较 可以逐像素比较…

基于MATLAB车牌识别系统设计

MATLAB车牌识别系统设计 实践目的 车牌是一辆汽车独一无二的信息,因此,对车辆牌照的识别技术可以作为 辨识一辆车最为有效的方法。随着ITS(智能交通系统)的高速发展,对车牌识别技术的研究也随之发展。从根本上讲,牌照识别应用了…

中缀表达式转后缀表达式(逆波兰表达式)及如何计算后缀表达式

目录 中缀、后缀表达式简介 中缀转后缀的规则 模拟中缀转后缀 中缀转后缀代码 后缀表达式求值 后缀表达式求值代码 Leetcode相关题目 中缀、后缀表达式简介 首先说说什么是中缀表达式,中缀表达式中,操作符是以中缀形式处于操作数的中间。例如&…