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

news2024/11/19 9:28:42

目录

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.coloradmin.cn/o/1475755.html

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

相关文章

如何调用GLM-4 API实现智能问答

诸神缄默不语-个人CSDN博文目录 GLM系列大模型是智谱AI提供的系列语言模型,GLM-4没有开源,只提供了API。本文介绍如何用Python语言调用GLM-4 API实现智能问答。 智谱AI为所有用户提供了18元免费额度,可以试用。 文章目录 1. 获得API key2. …

探索AI视频模型的无限可能:OpenAI的Sora引领创新浪潮

文章目录 📑前言一、技术解析二、应用场景三、未来展望四、伦理与创意五、用户体验与互动🌤️总结 📑前言 随着人工智能技术的蓬勃发展,AI视频模型正逐渐成为科技领域的新宠。在这个变革的浪潮中,OpenAI推出的首个AI视…

嵌入式C语言(四)

零长度数组 零长度数组、变长数组都是GNU C编译器支持的数组类型。 什么是零长度数组? 首先肯定长度是为0的数组 ANSI C规定定义一个数组长度必须为一个常数,那么就是这个数组的长度在编译的时候就确定了。 int a[10];但是在C99标准中规定可以定义一…

华为数通方向HCIP-DataCom H12-821题库(单选题:501-520)

第501题 三台交换机运行RSTP协议,拓扑和配置情况如图所示。那么以下关于根桥的描述,正确的是哪一项? A、根桥是SWA B、根桥是SWB C、根桥是SWC D、根桥无法确定 参考答案:A 第502题 在华为设备中,以下哪一个命令可以实现BFD与静态默认路由联动? A、ip route-static 0.…

加盟户用光伏赚钱吗?

光伏发电是一个新兴的投资领域,其中所蕴含的财富商机是非常多的,并且大多可靠效益显著,让无数创业投资者看到了生财的希望。 一、什么是户用光伏? 户用光伏,顾名思义,是在居民区安装的光伏发电系统。它利用太阳能电池板将光能转化为电能&…

InnoDB锁介绍

本文主要介绍MySQL InnoDB引擎中的各种锁策略和锁类别,并针对记录锁做演示以便于理解。 以下内容适用于MySQL 8.0版本。 读写锁 处理并发读/写访问的系统通常实现一个由两种锁类型组成的锁系统。这两种锁通常被称为共享锁(shared lock)和排他锁(exclusive lock)&…

网络编程-编码与解码(Protobuf)

编码与解码 下面的文字都来自于极客时间 为什么要编解码呢?因为计算机数据传输的是二进制的字节数据 解码:字节数据 --> 字符串(字符数据) 编码:字符串(字符数据)–> 字节数据 我们在编…

车载测试-常用adb命令和使用场景

app安装: 最常用 adb install apk地址 应用安装,常用于直接在系统上安装新包用于测试验证bug 常用参数(一般直接使用这三个参数一起) -t 允许测试包 -r 替换已存在的应用程序,也就是说强制安装 -d 允许进行将见状…

【.NET Core】深入理解IO之File类

【.NET Core】深入理解IO之File类 文章目录 【.NET Core】深入理解IO之File类一、概述二、File类2.1 File.AppendAllLines方法2.2 File.AppendAllText方法2.3 File.Copy 方法2.4 File.Create 方法2.5 File.Decrypt(String) 方法2.6 File.Delete(String) 方法2.7 File.Move 方法…

基于Python网络爬虫的IT招聘就业岗位数据分析可视化推荐系统

文章目录 基于Python网络爬虫的IT招聘就业岗位数据分析可视化推荐系统项目概述招聘岗位数据爬虫分析系统展示用户注册登录系统首页IT招聘数据开发岗-javaIT招聘数据开发岗-PythonIT招聘数据开发岗-AndroidIT招聘数据开发岗-其它招聘岗位数据分析算法方面运维方面测试方面招聘岗…

C语言 变量

变量其实只不过是程序可操作的存储区的名称。C 中每个变量都有特定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上。 变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下划线开头…

Python入门到精通(九)——Python数据可视化

Python数据可视化 一、JSON数据格式 1、定义 2、python数据和JSON数据转换 二、pyecharts 三、折线图 四、地图 五、动态柱状图 一、JSON数据格式 1、定义 JSON是一种轻量级的数据交互格式。可以按照JSON指定的格式去组织和封装数据JSON本质上是一个带有特定格式的字符…

python|闲谈2048小游戏和数组的旋转及翻转和转置

目录 2048 生成数组 n阶方阵 方阵旋转 顺时针旋转 逆时针旋转 mxn矩阵 矩阵旋转 测试代码 测试结果 翻转和转置 2048 《2048》是一款比较流行​的数字游戏​,最早于2014年3月20日发行。原版2048由Gabriele Cirulli首先在GitHub上发布,后被移…

LeetCode142. 环形链表 II刷题详解

今天力扣刷到了一个特别有意思的题目,于是就写了下面的题解来加深以下理解。 142. 环形链表 II - 力扣(LeetCode) 这个可以分为两大步去写,首先要判断链表是否有环,然后如果有环就去找到环的入口,没有环返…

11 PLL IP核

PLL IP 核简介 锁相环(PLL)作为一种反馈控制电路,其特点是利用外部输入的参考信号来控制环路内部震荡信号的频率和相位。因为锁相环可以实现输出信号频率对输入信号频率的自动跟踪,所以锁相环通常用于闭环跟踪电路。锁相环在工作…

一文读懂压敏电阻原理,参数,选型

大家好,我是砖一。 压敏电阻并不是一般的电阻,而是一种具有瞬态电压抑制功能的元件,效果同TVS。 这篇文章介绍压敏电阻的一些基本知识,包括参数、选型、应用等。 一,基础知识 压敏电阻用MY表示,MY后…

东方博宜 1078. 求恰好使s=1+1/2+1/3+…+1/n的值大于X时n的值

东方博宜 1078. 求恰好使s11/21/3…1/n的值大于X时n的值 #include<iostream> using namespace std; int main() {int x ;cin >> x ;double s 0 ;int i 1 ;bool m true ;while(m){s 1.0/i ;if(s>x)break;i ;}cout << i ;return 0 ; }

【C++那些事儿】深入理解C++类与对象:从概念到实践(上)| 揭开this指针的神秘面纱

&#x1f4f7; 江池俊&#xff1a; 个人主页 &#x1f525;个人专栏&#xff1a; ✅数据结构冒险记 ✅C那些事儿 &#x1f305; 有航道的人&#xff0c;再渺小也不会迷途。 文章目录 1. 面向过程和面向对象初步认识2.类的引入3.类的定义4.类的访问限定符及封装4.1 访问限定符…

SpringBoot案例(黑马学习笔记)

这个案例呢&#xff0c;就是Tlias智能学习辅助系统。 参考接口文档完成后端功能的开 发&#xff0c;然后结合前端工程进行联调测试即可。 完成后的成品效果展示&#xff1a; 准备工作 需求&环境搭建 需求说明 部门管理 部门管理功能开发包括&#xff1a; ● 查询部门列…

C++ 高频考点

1. C/C内存有哪几种类型&#xff1f; C中&#xff0c;内存分为5个区&#xff1a;堆(malloc)、栈(如局部变量、函数参数)、程序代码区&#xff08;存放二进制代码&#xff09;、全局/静态存储区&#xff08;全局变量、static变量&#xff09;和常量存储区&#xff08;常量&…