自旋锁与CAS
上文我们认识了许许多多的锁,此篇我们的CAS就是从上文的锁策略开展的新概念,我们来一探究竟吧
1. 什么是CAS?
CAS: 全称Compare and swap,字⾯意思:“比较并交换”,⼀个CAS涉及到以下操作:
我们假设内存中的原数据V,旧的预期值A,需要修改的新值B。
- 比较A与V是否相等。(比较)
- 如果比较相等,将B写入V。(交换)
- 返回操作是否成功。
CAS伪代码
下面写的代码不是原子的,真实的CAS是⼀个原子的硬件指令完成的.这个伪代码只是辅助理解CAS的工作流程.
while{boolean CAS(address, expectValue, swapValue) {if (&address == expectedValue) {&address = swapValue;return true;}return false;}
}
说明:
- address: 表示要修改的内存地址
- expectValue: 预期值
- swapValue:要设置的新值
执行流程:
- 用一个预期值去和内存中的值做比较
- 如果预期值与内存中的值相等,就用新的值更新内存中的值
- 如果预期值与内存中的值不相等,就用进入下一次比较
我们前面学习过的线程安全问题是由于CPU的随机调度导致的,我们的处理方法是给有关修改共享变量的代码块加了synchronize关键字,保证了原子性,解决了线程安全问题,
那么问题来了,
为什么我们看到的CAS伪代码并没有保证原子性,那么他是如何保证原子性的呢??
我们从一段例子来讲解:
总结:
- CAS对应的是硬件指令cmpxchg从CPU做了原子性支持,与LOCK 、UNLOCK 指令同地位
- CAS操作每次读取(LOAD)数据是从主内存中读取,并没有对应的工作内存
- 自旋锁是通过while把CAS进行包裹,让CAS没有成功的时候不停的执行,直到成功执行为止
- CAS是一个真正的指令,JVM调用了本地方法之后,对应的CAS指令会在CPU上执行
执行中LOAD数据会访问主内存
2. CAS实现自旋锁
基于CAS实现更灵活的锁,获取到更多的控制权.
自旋锁伪代码:
public class SpinLock {private Thread owner = null;public void lock(){// 通过CAS 看当前锁是否被某个线程持有.
// 如果这个锁已经被别的线程持有, 那么就⾃旋等待.
// 如果这个锁没有被别的线程持有, 那么就把owner 设为当前尝试加锁的线程.
}}while(!CAS(this.owner, null, Thread.currentThread())){}public void unlock (){this.owner = null;}
3. JDK中基于CAS实现的原子类
说明:
- JDK中提供了CAS实现的原子类,我们举其中一种
AtomicInteger类
来说明
- 我们调用其中的方法
getAndIncrement()
//自增 ++i
atomicInteger.getAndIncrement();
- 我们发现getAndIncrement()方法本质是
getAndIncrement()
public final int getAndIncrement() {return U.getAndAddInt(this, VALUE, 1);
}
- 其中调用了Unsafe这个类的方法,是JDK内部的工具类,不建议外部程序员直接调用
private static final Unsafe U = Unsafe.getUnsafe();
//获取内存中字段在对象中的偏移地址private static final long VALUE= U.objectFieldOffset(AtomicInteger.class, "value");
//1. o表示原子类对象
//2. offset表示内存中字段在对象中的偏移地址
//3. delta表示传入的值
public final int getAndAddInt(Object o, long offset, int delta) {int v;do {//v是CAS操作之前获取的预期值v = getIntVolatile(o, offset);} while (!weakCompareAndSetInt(o, offset, v, v + delta));//while不停的自旋return v;}
public final boolean weakCompareAndSetInt(Object o, long offset,//内存地址int expected,//预期值int x) {//要设置的值return compareAndSetInt(o, offset, expected, x);//最后调用compareAndSetInt()方法}
//native表示本地方法,即对应了一条CAS指令cmpxchg
public final native boolean compareAndSetInt(Object o, long offset,int expected,int x);
- 图解如下:
4. CAS的ABA问题
首先我们要知道什么是ABA问题???
ABA的问题:
假设存在两个线程t1和t2.有一个共享变量value,初始值为A.
接下来,线程t1想使用CAS把value值改成Z,那么就需要
先读取value的值,记录到oldValue变量中.
使用CAS判定当前value的值是否为A,如果为A,就修改成Z.
但是,在t1执行这两个操作之间,t2线程可能把value的值从A改成了B,又从B改成了A
举个例子: 今天是疯狂星期四,我和我的女友共用一个小荷包吃饭,我和她说如果小荷包里面有100元 (A状态) 晚上6点就点KFC吃,我怕她忘记了,我就先自己点了,此时,小荷包余额为50元 (B状态) 然后我的朋友疯狂星期四V我50,小荷包的钱又变回了100 (A状态),她再点的时候,发现小荷包还是100元,她就再点了一份,就导致多点了一份KFC
那么我们该如何解决ABA问题呢??
在点KFC的例子当中,我们可以让他查看一下消费记录(版本号)
,此时的100元是否是开始的100元,
由此可得,我们可以可以给value数据加上一个版本号,当线程2要修改时,不仅要value要符合预期值,同时版本号也要符合
即:
CAS操作在读取旧值的同时,也要读取版本号.
真正修改的时候,
- 如果当前版本号和读到的版本号相同,则修改数据,并把版本号+1.
- 如果当前版本号高于读到的版本号.就操作失败(认为数据已经被修改过了).
版本号可以用自增的数值,也可以用随机的UUID,也可以使用时间戳
System.out.println(UUID.randomUUID());
生成随机数:
相关文章:

自旋锁与CAS
上文我们认识了许许多多的锁,此篇我们的CAS就是从上文的锁策略开展的新概念,我们来一探究竟吧 1. 什么是CAS? CAS: 全称Compare and swap,字⾯意思:“比较并交换”,⼀个CAS涉及到以下操作: 我们假设内存中…...
数组-二分查找
目录 算法思想: 实践: 备注: 二分查找是一种高效的查找算法,适用于在 有序数组 或列表中快速定位目标元素的索引。 重要事情说三遍:使用前提:数组有序,无重复,如果数组未排序&am…...
如何使用 Python 进行文件读写操作?
大家好,我是 V 哥。今天的内容来介绍 Python 中进行文件读写操作的方法,这在学习 Python 时是必不可少的技术点,希望可以帮助到正在学习 python的小伙伴。 以下是 Python 中进行文件读写操作的基本方法: 一、文件读取࿱…...
springcloud中的Feign调用
目录 一、基础应用 1.feign使用 1.增加feign依赖 2.编写feign接口 3.启用feign 4.调试 5.可能出现的异常信息 1.404 可能原因: 2.503 可有原因: 2.feign自定义配置 1.创建Feign配置类 2.feign接口 3.调试结果 3.feign多参数请求 Feign是Netflix开发的声明…...
【部署】将项目部署到云服务器
目录 1.获得服务器 2.连接到云服务器 3.配置环境 3.1.Java(运行后端所需) 3.2.MySQL数据库 3.3.Nginx(运行前端所需) 3.4. Node.js(构建前端所需) 4.打包项目 4.1.打包后端项目 4.2.打包前端项目…...

2024年AI大模型技术年度总结与应用实战:创新与突破并进
前言 回顾2024年,我一共发布了286篇博文,粉丝数也达到了43000多。这一年里,我收获颇丰,始终坚持AI大模型的研究方向,并且积极开展大模型的实战应用,也取得了一系列令人振奋的突破。 在286篇博文中&#…...

docker离线安装及部署各类中间件(x86系统架构)
前言:此文主要针对需要在x86内网服务器搭建系统的情况 一、docker离线安装 1、下载docker镜像 https://download.docker.com/linux/static/stable/x86_64/ 版本:docker-23.0.6.tgz 2、将docker-23.0.6.tgz 文件上传到服务器上面,这里放在…...

SuperdEye:一款基于纯Go实现的间接系统调用执行工具
关于SuperdEye SuperdEye是一款基于纯Go实现的间接系统调用执行工具,该工具是TartarusGate 的修订版,可以利用Go来实现TartarusGate 方法进行间接系统调用。 该工具的目标是为了扫描挂钩的NTDLL并检索Syscall编号,然后使用它来执行间接系统调…...

PCL 新增自定义点类型【2025最新版】
目录 一、自定义点类型1、前言2、定义方法3、代码示例二、合并现有类型三、点云按时间渲染1、CloudCompare渲染2、PCL渲染博客长期更新,本文最近更新时间为:2025年1月18日。 一、自定义点类型 1、前言 PCL库自身定义了很多点云类型,但是在使用的时候时如果要使用自己定义的…...

Docker导入镜像
使用命令行进行处理: docker load < onething1_wxedge.tar如下图所示 查看状态 docker images...

PyTorch使用教程(9)-使用profiler进行模型性能分析
1、简介 PyTorch Profiler是一个内置的性能分析工具,可以帮助开发者定位计算资源(如CPU、GPU)的瓶颈,从而更好地优化PyTorch程序。通过捕获和分析GPU的计算、内存和带宽利用情况,能够有效识别并解决性能瓶颈。 2、原…...

SpringBoot中使用MyBatis-Plus详细介绍
目录 一、MyBatis-Plus的使用步骤 1.引入MybatisPlus的起步依赖 2.定义Mapper(也叫dao)层的接口 3.MyBatis-Plus中常用注解 4. 使用MyBatis-Plus时要做如下配置 5.条件构造器 Wrapper 一、MyBatis-Plus的使用步骤 1.引入MybatisPlus的起步依赖 M…...

PCL 部分点云视点问题【2025最新版】
目录 一、问题概述二、解决方案1、软件实现2、代码实现三、调整之后博客长期更新,本文最近更新时间为:2025年1月18日。 一、问题概述 针对CloudCompare软件处理过的pcd格式点云,在使用PCL进行特征点提取、配准等实验中最终显示结果出现点云位置偏差较大的问题,本博客给出解…...

【Linux】常见指令(三)
Linux常见指令 01.nano02.cat03.cp04.mv 我的Linux专栏:【Linux】 本节Linux指令讲解的基本框架如下: 大家可以根据自己的需求,自行进行跳转和学习! 01.nano nano Linux 系统中一款简单易用的命令行文本编辑器,适合…...
第5章:Python TDD定义Dollar对象相等性
写在前面 这本书是我们老板推荐过的,我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后,我突然思考,对于测试开发工程师来说,什么才更有价值呢?如何让 AI 工具更好地辅助自己写代码,或许…...

nuxt3项目打包部署到服务器后配置端口号和开启https
nuxt3打包后的项目部署相对于一般vite打包的静态文件部署要稍微麻烦一些,还有一个主要的问题是开发环境配置的.env环境变量在打包后部署时获取不到,具体的解决方案可以参考我之前文章 nuxt3项目打包后获取.env设置的环境变量无效的解决办法。 这里使用的…...

MongoDB文档查询
一、实验目的 1. 理解MongoDB文档数据库的基本概念和特性。 2. 掌握在MongoDB中创建集合和插入文档数据的方法。 3. 学习使用MongoDB进行文档查询操作,包括查询、过滤和排序等。 二、实验环境准备 1. JAVA环境准备:确保Java Development Kit (J…...

【GORM】初探gorm模型,字段标签与go案例
GORM是什么? GORM 是一个Go 语言 ORM(对象关系映射)库,它让我们可以使用结构体来操作数据库,而无需编写SQL 语句 GORM 模型与字段标签详解 在 GORM 中,模型是数据库表的抽象表示,字段标签&am…...

Windows下的Milvus安装秘籍:向量数据库轻松上手
目录 一、简介 二、dockers的安装 1.介绍 2.环境准备 1.启动WSL 的功能。 2.安装并启动Hyper-V Windows10下的安装办法: Windows11下的安装办法: 启动Hyper-V 3.Docker的安装 4、验证是否安装成功 三、安装Milvus 1.Milvus下载 2.Milvus启动…...
在GUI中添加一个Label
标签是一种非常简单的小部件,它可以为我们的图形用户界面(GUI)增添价值。它可以阐释其他组件的用途,提供一些额外的信息,这可以引导用户理解输入框组件的含义,也能够解释那些无需用户输入数据的组件所显示数据的含义。 准备就绪 我们将扩展第一个应用案例,即《创建第一…...

AUTOSAR实战教程--标准协议栈实现DoIP转DoCAN的方法
目录 软件架构 关键知识点 第一:PDUR的缓存作用 第二:CANTP的组包拆包功能 第三:流控帧的意义 配置过程 步骤0:ECUC模块中PDU创建 步骤1:SoAD模块维持不变 步骤2:DoIP模块为Gateway功能添加Connection 步骤3:DoIP模块为Gateway新增LA/TA/SA 步骤4:PDUR模…...
Python实例题:Python计算微积分
目录 Python实例题 题目 代码实现 实现原理 符号计算: 数值计算: 可视化功能: 关键代码解析 1. 导数计算 2. 积分计算 3. 微分方程求解 4. 函数图像绘制 使用说明 安装依赖: 基本用法: 示例输出&#…...

【Linux操作系统】基础开发工具(yum、vim、gcc/g++)
文章目录 Linux软件包管理器 - yumLinux下的三种安装方式什么是软件包认识Yum与RPMyum常用指令更新软件安装与卸载查找与搜索清理缓存与重建元数据 yum源更新1. 备份现有的 yum 源配置2. 下载新的 repo 文件3. 清理并重建缓存 Linux编辑器 - vim启动vimVim 的三种主要模式常用操…...

DAY 44 预训练模型
知识点回顾: 预训练的概念常见的分类预训练模型图像预训练模型的发展史预训练的策略预训练代码实战:resnet18 (一)预训练的概念 我们发现准确率最开始随着epoch的增加而增加。随着循环的更新,参数在不断发生更新。 所以…...
bat批量去掉本文件夹中的文件扩展名
本文本夹内 批量去掉本文件夹中的文件扩展名 假如你有一些文件,你想去掉他们的扩展名 有没有方便的办法呢 今天我们就分享一种办法。 下面,就来看看吧。 首先我们新建一个记事本,把名字改为,批量去掉本文件夹中的文件扩展名.txt 然…...

Keil开发STM32生成hex文件/bin文件
生成hex文件生成bin文件 STM32工程的hex文件和bin文件都可以通过Keil直接配置生成 生成hex文件 工程中点击魔术棒,在 Output 中勾选 Create HEX File 选项,OK保存工程配置 编译工程通过后可以看到编译输出窗口有创建hex文件的提示 默认可以在Output文…...
Jetpack Compose 中,DisposableEffect、LaunchedEffect 和 sideEffect 区别和用途
在 Jetpack Compose 中,DisposableEffect、LaunchedEffect 和 sideEffect 都是用于处理副作用(Side Effects)的 API,但它们的用途和触发时机不同。以下是它们的核心概念和区别: 1. 副作用(Side Effect&…...
rk3588 区分两个相同的usb相机
有时候会插入两个一模一样的usb相机,担心每次启动他们所对应的设备节点 /dev/video* 会变化,所以需要绑定usb口,区分两个相机。把两个相机都插入后,查看usb信息 rootrk3588:/# udevadm info --attribute-walk --name/dev/video0U…...
elasticsearch基本操作笔记
1.通过kibana查看elasticsearch版本信息 a.左上角三道横->Management->Dev Tools b.GET / 执行 c.执行结果 { “name” : “xxxx”, “cluster_name” : “xxxxxxx”, “cluster_uuid” : “vl1UudAoQp-aHWAzyPoMyw”, “version” : { “number” : “7.15.1”, “build…...
Vue-Todo-list 案例
一、前言 在前端开发中,Todo List(待办事项列表) 是一个非常经典的入门项目。它涵盖了组件化思想、数据绑定、事件处理、本地存储等核心知识点,非常适合用来练习 Vue 的基本用法。 本文将带你一步步实现一个功能完整的 Vue Todo…...