【Java多线程】面试常考——锁策略、synchronized的锁升级优化过程以及CAS(Compare and swap)

目录

1、锁的策略

1.1、乐观锁和悲观锁 

1.2、轻量级锁和重量级锁

1.3、自旋锁和挂起等待锁

1.4、普通互斥锁和读写锁

1.5、公平锁和非公平锁

1.6、可重入锁和不可重入锁 

2、synchronized 内部的升级与优化过程

2.1、锁的升级/膨胀

2.1.1、偏向锁阶段

2.1.2、轻量级锁阶段

2.1.3、重量级锁阶段

2.2、锁消除

2.3、锁粗化

3、CAS(Compare and swap)

3.1、CAS 的应用

3.1.1、实现 Atomic 原子类

3.1.2、实现自旋锁

3.1.3、CAS 的 ABA 问题



1、锁的策略

加锁过程中,处理冲突的过程中,涉及到的一些不同的处理方式,就叫锁的策略。

1.1、乐观锁和悲观锁 

乐观锁
在加锁之前,预估当前出现锁冲突的概率不大,因此在进行加锁的时候就不会做太多的工作。

由于加锁过程中做的事情比较少,加锁的速度可能就更快,但是更容易引入一些其他的问题(消耗更多cpu资源)。


悲观锁
在加锁之前,预估当前出现锁冲突的概率比较大,因此在进行加锁的时候就会做更多的工作。

由于加锁过程中做的事情更多,加速的速度可能更慢,但是整个过程中不容易出现其他问题

1.2、轻量级锁和重量级锁

轻量级锁
加锁的开销小,加锁的速度更快, 轻量级锁,一般就是乐观锁。


重量级锁
加锁的开销更大,加锁的速度更慢,重量级锁,一般就是悲观锁。

需要注意的是: 

悲观乐观,是在加锁之前,对未发生的事情进行的预估。
轻量重量,是在加锁之后,对结果的评价。
但是整体来说,这两种角度,描述的都是同一个事情。 

1.3、自旋锁和挂起等待锁

自旋锁
就是轻量级锁的一种典型实现,同时也是一种乐观锁。
进行加锁的时候,会搭配一个while循环,如果加锁成功,自然循环结束。
如果加锁不成功,不是阻塞放弃cpu,而是进行下一个循环,再次尝试获取到锁。
上述操作的反复快速执行的过程,就称为“自旋”。一旦其他线程释放了锁,就能第一时间拿到锁。


挂起等待锁
就是重量级锁的一种典型实现,同时也是一种悲观锁(适用于锁冲突激烈的情况)。

1.4、普通互斥锁和读写锁

普通互斥锁
类似于 synchronized 操作涉及的 加锁 和 解锁


读写锁
把加锁分为两种情况: 1)加读锁 ReadLock 2)加写锁 WriteLock
结论:读锁与读锁不会出现锁冲突(不会阻塞),写锁与写锁、读锁与写锁都会出现锁冲突(会阻塞)
这里的差异理解起来也很简单,因为两个线程进行读操作,本身就是线程安全的,所以不需要进行互斥(而且读操作在实际开发中有非常频繁,所以能够大大提升了性能), synchronized 不是读写锁,对于读写锁 JVM 同样封装了 api 给程序员使用。

1.5、公平锁和非公平锁

公平锁
先来后到叫公平,实现公平锁需要引入额外的数据结构(队列,记录先后顺序)。


非公平锁
Java中默认的锁就是非公平锁,正是因为非公平锁导致了“线程饿死”

所谓“线程饿死”,可以理解为:

        当线程A获取锁时,发现此时不能完成实质性的逻辑(取钱时发现atm没钱了),此时只能释放锁并退出,重新与其他线程竞争该锁(刚刚释放锁的线程也会参与到锁竞争中)。有一个极端情况,就是每次释放锁之后又是线程A获取到了锁,并且此时依然无法完成实质性的逻辑,又只能释放锁。就出现【线程A忙了半天不干实事,其他线程都只能干等了】的情况,这就称之为“线程饿死”。
        而这种极端情况出现的概率还挺高的,因为线程A获取锁后处在RUNNABLE状态,而其他线程处在BLOCKED状态,处于BLOCKED状态的线程需要系统唤醒之后才能参与锁竞争,而线程A不需要,因此线程A再次获取到锁的概率比其他的线程高。

1.6、可重入锁和不可重入锁 

针对一个线程一把锁,连续加锁两次不会产生死锁,就是可重入锁,反之就是不可重入锁。系统自带的锁是不可重入锁,Java 的 synchronized 是可重入锁。

2、synchronized 内部的升级与优化过程

Java 中的 synchronized 具有自适应能力。内部会自动评估当前锁冲突的剧烈程。

如果当前锁冲突的剧烈程度不大,就处在 乐观锁/轻量级锁/自旋锁。

如果当前锁冲突的剧烈程度很大,就处在 悲观锁/重量级锁/挂起等待锁。

2.1、锁的升级/膨胀

2.1.1、偏向锁阶段

类似于“懒汉模式”,能不加锁就不加锁,能晚加锁就晚加锁。在遇到竞争的情况下,偏向锁没有提高效率;但是如果在没有竞争的情况下, 偏向锁就大幅度的提高了效率。

.

【只是做了一个标记,没有真加锁(也就不存在互斥),一旦有其他线程想要来竞争锁,就在另一个线程之前先把锁获取到,进而从偏向锁升级到轻量级锁(存在互斥)。】

2.1.2、轻量级锁阶段

通过自旋锁的方式来实现优势:其他线程释放锁后能够第一时间拿到锁;劣势:消耗cpu;

.

此外 synchronized 内部也会统计当前这个锁对象上,有多少个线程产于竞争(都是自旋式竞争),如果发现参与竞争的线程比较多,就会进一步升级到重量级锁。

2.1.3、重量级锁阶段

此时拿不到锁的线程就不会继续自旋,而是进入“阻塞等待”,让出cpu资源。锁被释放后回归随机唤醒机制。

2.2、锁消除

简单来说就是自动干掉不必要的锁。

2.3、锁粗化

简单来说就是把多个细粒度的锁合并成一个粗粒度的锁,减少锁竞争的开销。

以上说的所有机制,都是在内部自动执行的,不需要程序员在编写代码的时候真正的手动执行。

3、CAS(Compare and swap)

CAS(即比较并交换)本身是特殊的单个 cpu 指令,一个 CAS 涉及以下操作:

假设内存中的原数据V,旧的预期值A,需要修改的新值B

1、比较A与V是否相等(比较)

2、如果比较相等,将B写入V(交换)

3、返回操作是否成功

由于 CAS是单个 cpu 指令,使用 CAS 可以“原子”的完成很多复杂操作,不涉及到加锁,也不会阻塞,合理的使用也可以保证线程安全,使用 CAS 的编程方式称为“无锁化编程”,是多线程编程中的特殊技巧。

操作系统对 CAS 指令进行了封装成了 api ,JVM 有对操作系统提供的 api 又封装一层。

3.1、CAS 的应用

3.1.1、实现 Atomic 原子类

Java 标准库中的 Atomic 原子类就是基于 CAS 来实现的。

伪代码描述 CAS 以及 模拟 AtomicInteger 自增过程

3.1.2、实现自旋锁

伪代码描述自旋锁的执行过程

3.1.3、CAS 的 ABA 问题

CAS 使用的时候,关键要点是判定当前内存的值和寄存器中的值是否一样,判定是否一样的操作本质上是在判定这个代码执行的过程中是否有其他线程穿插进来了。

但是这样的判定可能存在这样的可能,本来数值是0,执行 CAS 之前,另一个线程把值从 0 → 100 ,又从 100 → 0 ,虽然改了,但是又改回去了,这种情况就称为 ABA 问题。CAS 的 ABA 问题一般情况下不会有问题,不会产生啥 bug ,但是就怕一些极端情况。

解决方案:
1、约定数据变化是单向的(只能添加或者只能减少),不能是双向的。
2、对于本身就必须双向变化的数据,可以引入一个版本号,版本号是单向增加的。

【博主推荐】 

【Java多线程】对线程池的理解并模拟实现线程池-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/136160003?spm=1001.2014.3001.5501【Java多线程】分析线程加锁导致的死锁问题以及解决方案-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/136150237?spm=1001.2014.3001.5501【Java多线程】线程安全问题与解决方案-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/136133785?spm=1001.2014.3001.5501

如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

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

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

相关文章

2024年阿里云2核4G云服务器性能如何?价格便宜有点担心

阿里云2核4G服务器多少钱一年?2核4G服务器1个月费用多少?2核4G服务器30元3个月、85元一年,轻量应用服务器2核4G4M带宽165元一年,企业用户2核4G5M带宽199元一年。本文阿里云服务器网整理的2核4G参加活动的主机是ECS经济型e实例和u1…

Groovy(第五节) Groovy 之集合

Groovy 可以直接在语言内使用集合。在 Groovy 中,不需要导入专门的类,也不需要初始化对象。集合是语言本身的本地成员。Groovy 也使集合(或者列表)的操作变得非常容易,为增加和删除项提供了直观的帮助。 可以将范围当作集合 在前一节学习了如何用 Groovy 的范围将循环变得…

OpenHarmony 项目实战:智能体重秤

一、简介 本 demo 基于 OpenHarmony3.1Beta 版本开发,该样例能够接入数字管家应用,通过数字管家应用监测体重秤上报数据,获得当前测量到的体重,身高,并在应用端形成一段时间内记录的体重值,以折线图的形式…

皇冠测评:网络电视盒子哪个品牌好?电视盒子排行榜

欢迎各位来到我们的测评频道,本期我们要分享的产品是电视盒子,因很多网友留言不知道网络电视盒子哪个品牌好,我们通过为期一个月的测评后整理了电视盒子排行榜,想买电视盒子的可以看看下面这五款产品,它们各方面表现非…

【JGit 】一个完整的使用案例

需求 生成一系列结构相同的项目代码,将这些项目的代码推送至一个指定的 Git 仓库,每个项目独占一个分支。 推送时若仓库不存在,则自动创建仓库。 分析 生成代码使用 Java 程序模拟,每个项目中模拟三个文件。Project.cpp 、Pro…

更改elementui的箭头图片以及位置

//更改箭头位置 .el-tree-node__content > .el-tree-node__expand-icon {position: absolute;right: 12px; }//更改箭头图片 .el-tree-node__expand-icon {transform: rotate(-90deg); } .el-tree-node__expand-icon.expanded {transform: rotate(0deg); } // 有子节点 且已…

QEMU之内存虚拟化

内存虚拟化方案 最直观的方案,将QEMU进程的虚拟地址空间的一部分作为虚拟机的物理地址。但该方案有一个问题: 在物理机上,CPU对内存的访问在保护模式下是通过分段分页实现的,在该模式下,CPU访问时使用的是虚拟地址&am…

Unity绘制六边形体

现在steam上面有很多下棋类/经营类的游戏都是用六边形的地形,比较美观而且实用,去年在版本末期我也自己尝试做了一个绘制六边体的demo,一年没接触unity竟然都要忘光了,赶紧在这边记录一下。 想cv代码可以直接拉到代码章节 功能 …

音频转换器哪个好?3款电脑软件+3款手机应用

在当今的数字时代,音频转换已成为许多用户日常的需求。为了帮助您找到最佳的音频转换工具,我们将介绍3款电脑软件和3款手机应用。这些工具都各有特点,能够满足不同用户的需求。 1.电脑软件篇 1.1金舟音频大师 金舟音频大师是一款多功能的音…

【LabVIEW 】串口如何读取长度不一致的字符串

工程经验 1、在循环中,加入定时器,这样可以一段时间读取一次。 2、只要获取完整的一帧数据,就可以进行过滤筛选。

力扣128. 最长连续序列(哈希表)

Problem: 128. 最长连续序列 文章目录 题目描述思路复杂度Code 题目描述 思路 1.先将数组中的元素存入到一个set集合中(去除重复的元素) 2.欲找出最长连续序列(先定义两个int变量longestSequence和currentSequence用于记录最长连续序列和当前…

Adobe Acrobat DC中如何合并pdf并生成目录

一、利用 Acrobat 合成pdf目录 (一)新建标签(更改标签等级等) 1,用Adobe acrobat 软件打开待添加书签的pdf文档。 2,打开之后点击软件左边栏的书签(有时被隐藏了,点击一下界面左边…

第一节 数据操作+数据处理

本系列文章为李沐老师《动手学深度学习》Pytorch版实践学习笔记,相关课程教学、书籍、代码均为开源,可通过以下链接参考学习: 跟李沐学AI的个人空间-跟李沐学AI个人主页-哔哩哔哩视频 (bilibili.com) 前言 — 动手学深度学习 2.0.0 documenta…

高校物品捐赠管理系统|基于springboot高校物品捐赠管理系统设计与实现(源码+数据库+文档)

高校物品捐赠管理系统目录 目录 基于springboot高校物品捐赠管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户信息管理 2、捐赠信息管理 3、论坛信息管理 4、公告信息管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算…

RabbitMQ讲解与整合

RabbitMq安装 类型概念 租户 RabbitMQ 中有一个概念叫做多租户,每一个 RabbitMQ 服务器都能创建出许多虚拟的消息服务器,这些虚拟的消息服务器就是我们所说的虚拟主机(virtual host),一般简称为 vhost。 每一个 vhos…

NLP-词向量、Word2vec

Word2vec Skip-gram算法的核心部分 我们做什么来计算一个词在中心词的上下文中出现的概率? 似然函数 词已知,它的上下文单词的概率 相乘。 然后所有中心词的这个相乘数 再全部相乘,希望得到最大。 目标函数(代价函数&#xff0…

如何用CDH+Apache DolphinScheduler开启Kerberos

搭建环境 多台linux主机搭建集群CDH 6.3.2 (Parcel)版本Apache DolphinScheduler1.3.2版本,本流程在CDH已搭建完成并可正常使用后,开启kerberos功能,Apache DolphinScheduler用于大数据任务管理与执行,是很不错的任务调度平台&am…

ZYNQ--MIG核配置

文章目录 MIG核配置界面多通道AXI读写DDR3MIG核配置界面 Clock Period: DDR3 芯片运行时钟周期,这个参数的范围和 FPGA 的芯片类型以及具体类型的速度等级有关。本实验选择 1250ps,对应 800M,这是本次实验所采用芯片可选的最大频率。注意这个时钟是 MIG IP 核产生,并输出给…

压缩视频大小的软件有哪些?5款软件推荐

压缩视频大小的软件有哪些?随着高清摄像设备的普及和网络速度的不断提升,视频文件变得越来越庞大,动辄数百兆甚至数GB的大小常常让用户在分享和存储时感到头疼。幸运的是,市面上有许多优秀的视频压缩软件可以帮助我们轻松应对这一…

NFS服务器挂载失败问题

问题 mount.nfs: requested NFS version or transport protocol is not supported背景:现在做嵌入式开发,需要在板端挂载服务器,读取服务器文件。挂载中遇到该问题。 挂载命令长这样 mount -t nfs -o nolock (XXX.IP):/mnt/disk1/zixi01.ch…