JavaEE:多线程进阶(CAS)

news2024/11/23 9:02:47

文章目录

  • CAS
    • 什么是 CAS
      • CAS 伪代码
    • CAS有哪些应用
    • CAS的ABA问题
      • 什么是ABA问题
      • ABA问题带来的BUG
      • 解决方案


CAS

什么是 CAS

CAS: 全称Compare and swap,字面意思:”比较并交换“,一个 CAS 涉及到以下操作:

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

  1. 比较 A 与 V 是否相等。(比较)
  2. 如果比较相等,将 B 写入V。(交换)
  3. 返回操作是否成功。

CAS 伪代码

下面写的代码不是原子的, 真实的 CAS 是⼀个原子的硬件指令完成的. 这个伪代码只是辅助理解 CAS的工作流程.

    boolean CAS(address, expectValue, swapValue) {
        if (&address == expectedValue){
            &address = swapValue;
            return true;
        }
        return false;
    }

两种典型的不是"原子性"的代码:

  1. check and set (if 判定然后设定值)[上面的CAS就是这种形式]
  2. read and update (i++)

当多个线程同时对某个资源进行CAS操作,只能有一个线程操作成功,但是并不会阻塞其他线程,其他线程只会收到操作失败的信号.

CAS是乐观锁的一种实现方式

CAS有哪些应用

  1. 实现原子类
    标准库中提供了java.util.concurrent.atomic包,里面的类都是这种方式来实现的.
    在这里插入图片描述

    典型的就是AtomicInteger类,其中的getAndIncrement相当于i++操作

    AtomicInteger atomicInteger = new AtomicInteger(0);
    // 相当于 i++
    atomicInteger.getAndIncrement();
    

    伪代码实现:

    class AtomicInteger {
    	private int value;
    
    	public int getAndIncrement() {
    		int oldValue = value;
    		while( CAS( value, oldValue, oldValue+1) != true ) {
    			oldValue = value;
    		}
    		return oldValue;
    	}
    }
    

    假设两个线程同时调用getAndIncrement
    1. 两个线程都读取value的值到oldValue中.(oldValue是一个局部变量,在栈上,每个线程有自己的栈)
    在这里插入图片描述
    2. 线程1先执行CAS操作.由于oldValue和value的值相同,直接进行对value赋值.

    CAS是直接读写内存的,而不是操作寄存器.
    CAS的读内存,比较,写内存操作是一条硬件指令,是原子的.

    在这里插入图片描述
    3. 线程2再执行CAS操作,第一次CAS的时候发现oldValue和value不相等,不能进行赋值,因此需要进入循环.
    在循环里重新获取value的值赋给oldValue
    在这里插入图片描述
    4. 线程2接下来第二次执行CAS,此时oldValue和value相同,于是直接执行赋值操作.
    在这里插入图片描述
    5. 线程1和线程2返回各自的oldValue的值即可.
    通过形如上述代码就可以实现一个原子类.不需要使用重量级锁,就可以高效的完成多线程的自增操作.

    本来check and set 这样的操作在代码角度不是原子的,但是在硬件层面上可以让一条指令完成这个操作,也就变成原子的了

  2. 实现自旋锁
    基于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;
     	}
    }
    

CAS的ABA问题

什么是ABA问题

ABA问题:
假设存在两个线程t1和t2,有一个共享变量num,初始值为A.
接下来,线程t1想使用CAS把num的值改成Z,那么就需要

  1. 先读取num的值,记录到oldNum变量中
  2. 使用CAS判定当前num的值是否为A,如果为A,就修改成Z.
    但是,在t1执行这两个操作之间,t2线程可能把num的值从A改成了B,又从B改成了A.

线程t1的CAS是期望num不变就修改.但是num的值已经被t2给修改了,只不过又改成A了,这个时候t1究竟是否要更新num的值为Z呢?

到这一步,t1线程无法区分当前这个变量始终是A,还是经历了一个变化过程.
在这里插入图片描述

ABA问题带来的BUG

大部分的情况下,t2线程这样的一个反复横跳改动,对于t1是否修改num是没有影响的,但是不排除一些特殊情况.

假设我有100存款,我想从ATM取50.取款机创建了两个线程,并发的来执行-50操作.
我们期望一个线程执行-50成功,另一个线程-50失败.
如果使用CAS的方式来完成这个扣款的过程就可能会出现问题.

正常的过程

  1. 存款100.线程1获取到当前存款值为100,期望更新为50;线程2获取到当前存款值为100,期望更新为50.
  2. 线程1执行扣款成功,存款被改成50,线程2阻塞等待中.
  3. 轮到线程2执行了,发现当前存款为50,和之前读到的100不相同,执行失败.

异常的过程

  1. 存款100.线程1获取到当前存款值为100,期望更新为50;线程2获取到当前存款值为100,期望更新为50.
  2. 线程1执行扣款成功,存款被改成50,线程2阻塞等待中.
  3. 在线程2执行之前,我的朋友正好给我转了50,于是我的账户余额就变成了100!
  4. 轮到线程2执行了,发现当前存款为100,和之前读到的100相同,再次执行扣款操作

这个时候,扣款操作被执行了两次!都怪ABA!!!

解决方案

通过CAS也不是不能解决上述问题.

ABA是因为"余额"能加也能减,才会有ABA问题,如果只能加,不能减就可以解决上述问题.

哎呀,你这不是废话吗,余额怎么可能只加不减?!

别急,余额确实不可能,但是我们可以新建一个变量,让它只增不减.给它起个名字,就叫"版本号"好了.

具体操作如下:
我们可以给要修改的值,引入版本号,在CAS比较数据和当前值和旧值的同时,也要比较版本号是否符合预期.

  1. 如果当前版本号和读到的版本号相同,则修改数据,并把版本号+1.
  2. 如果当前版本号高于读到的版本号,那就操作失败(认为数据已经被修改过了)

在Java标准库中提供了AtomicStampedReference<E>类,这个类可以对某个类进行包装,在内部就提供了上面描述的版本管理功能.

本文到这里就结束啦~

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

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

相关文章

【Python报错已解决】`Provisional headers are shown Learn more`

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 引言&#xff1a;一、问题描述&#xff1a;1.1 报错示例&#xff1a;1.2 报错分析&#xff1a;1.3 解决思路&#xff…

软件测试面试如何正确谈薪

又是一波离职高峰&#xff0c;很多小伙伴已经开始投身跳槽的准备中了。大家选择跳槽无非是想增加自己的工资收入&#xff0c;所以面试过程中的谈薪环节就显得尤为重要&#xff0c;谈的好与不好&#xff0c;未来整个的薪资水平都可能受影响。 那面试中&#xff0c;当问到“你的…

SprinBoot+Vue二手回收微信小程序的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue3.6 uniapp代码 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平…

【C++】vector的简单模拟实现

目录 一、vector的基本实现机制&#xff1a; 二、vector的部分接口模拟实现&#xff1a; 1、构造与析构&#xff1a; 1、普通构造&#xff1a; 2、拷贝构造&#xff1a; 3、析构函数&#xff1a; 2、关于扩容&#xff1a; 1、reserve&#xff1a; 2、resize 3、增删查…

SpringCloud开发实战(六):Feign的最佳实践

目录 SpringCloud开发实战&#xff08;一&#xff09;&#xff1a;搭建SpringCloud框架 SpringCloud开发实战&#xff08;二&#xff09;&#xff1a;通过RestTemplate实现远程调用 SpringCloud开发实战&#xff08;三&#xff09;&#xff1a;集成Eureka注册中心 SpringCloud开…

基于SpringBoot的高校BBS在线互动论坛系统

&#x1f4a5;&#x1f4a5;源码和论文下载&#x1f4a5;&#x1f4a5;&#xff1a;基于SpringBoot的高校BBS在线互动论坛系统-源码论文报告数据库.rar 1. 系统介绍 本论文设计并实现了一个基于Spring Boot和Vue的校园论坛系统&#xff0c;该系统分为用户和管理员两个角色。用户…

9/4 链表-力扣 234、19

234.回文链表 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表&#xff1b;如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 输入&#xff1a;head [1,2,2,1] 输出&#xff1a;true 思考&#xff1a;链表遍历只能从前往后&a…

Android studio 更换下载的gradle

首先我们下载gradle 打个比方如果我们下载了一个github上的项目&#xff0c;而它使用的是gradle-6.5-bin.zip https://services.gradle.org/distributions/gradle-6.5-bin.zip 用浏览器去下载&#xff0c;可能需要翻墙 解压到电脑里 找到setting里的这一项&#xff0c;设置…

plc1200 weiluntong001

快接口 快代码 main代码 电脑IP地址 编译&#xff0c;启动仿真&#xff0c;下载到仿真PLCsim 必要时候可以设备离线。 打开并监视块。 打开netto plcsim 添加 本机IP&#xff0c;选择&#xff0c;双击。 PLC启动仿真之后&#xff0c;出现这个IP地址&#xff0…

88、k8s之pv+pvc

一、pv和pvc pv pv&#xff1a;Persistent volume 是k8s虚拟化的存储资源&#xff0c;实际上就是存储&#xff0c;例如本地的硬盘&#xff0c;网络文件系统&#xff08;nfs&#xff09; lvm RAID oss&#xff08;ceph&#xff09; 云存储。 pvc pvc&#xff1a;Persisten…

关于SPI通信失败的一种情况(CRC校验不匹配的问题)

问题 该项目中&#xff0c;使用外置的ADC芯片采集电压电流&#xff0c;主控MCU通过SPI与ADC芯片通信。调试时&#xff0c;SPI通信一直失败&#xff0c;与之前成功的项目对比&#xff0c;发现是SPI配置的问题。 void MX_SPI2_Init(void) {/* USER CODE BEGIN SPI2_Init 0 *//*…

2024.9计算机视觉设计开发工程师专项培训通知

为进一步贯彻落实中共中央印发《关于深化人才发展体制机制改革的意见》和国务院印发《关于“十四五”数字经济发展规划》等有关工作的部署要求&#xff0c;深入实施人才强国战略和创新驱动发展战略&#xff0c;加强全国数字化人才队伍建设&#xff0c;持续推进人工智能从业人员…

fastadmin 文件上传腾讯云

1-安装腾讯云SDK composer require qcloud/cos-sdk-v5 2-腾讯云配置 <?phpnamespace app\common\controller;use Qcloud\Cos\Client; use think\Controller; use think\Db;class Tencent extends Controller {/*** 上传文件* param $config* param $key* return array*/p…

微信公众号《GIS 数据工程:开始您的 ETL 之旅 》 文章删除及原因

微信公众号多次限制付费文章发布&#xff0c;不太明确其原因。我猜可能是得罪了某位大神&#xff0c;这倒是也不是不可能。我这说话口无遮拦&#xff0c;得罪几个人偶尔搞我一下也是应该的 。当然也可能是部分喜欢白嫖的网友一看我收费就不太高兴&#xff0c;偶尔做点小动作也是…

Windows系统下苹果虚拟机系统的安装

前言 搞苹果软件开发&#xff0c;未必要购买贵的苹果电脑。可以安装黑苹果系统&#xff0c;也可以安装VMware的苹果虚拟机。而且通过我的实践发现&#xff0c;目前苹果虚拟机的效果很不错。 1、参考文档链接 VM虚拟机怎么安装mac os&#xff1f;&#xff08;全教程&#xff0…

【LeetCode】03.无重复字符的最长子串

题目要求 做题链接3.无重复字符的最长子串 解题思路 我们通过参考给出的输入很容易就会从每一个字符开始&#xff0c;看看最长能延续多长。我们通过画图发现一旦一个字符可以延续到另一个字符&#xff0c;那么我们就不需要考虑他中间仍然存在字符重复的问题。因此而后我们发…

部署mongosh教程

1、上传软件包 将软件包上传到/usr/local目录下 部署 2.1 解压 tar zxvf mongosh-2.3.0-linux-x64.tgz 2.2 修改名称 mv mongosh-2.3.0-linux-x64/ mongosh 2.3 将 bin 目录中 mongosh 二进制文件复制到 PATH 变量中列出的目录中 sudo cp mongosh /usr/local/bin/ sudo cp …

第九届“创客中国”生成式人工智能中小企业创新创业大赛招商推介圆满落幕

金秋九月,丹桂飘香。9月2日晚,第九届“创客中国”生成式人工智能(AIGC)中小企业创新创业大赛招商推介会在南昌高新区艾溪湖畔成功举办。南昌市政府副秘书长、办公室党组成员陈吉炜出席并致辞。市中小企业局党组书记、市工信局党组书记、局长骆军出席。南昌高新区党工委委员、管…

16 C语言连接

使用c语言连接mysql&#xff0c;需要使用mysql官网提供的库&#xff0c;可以在官网下载 准备工作&#xff1a; 保证mysql服务有效 官网下载合适的mysql connect库 也可以直接安装mysql服务 yum install -y mysql-devel Connector/C使用 库格式如下&#xff1a; [hbMiWiFi-R1…

无线麦克风推荐,无线麦克风十大名牌,领夹麦克风十大品牌推荐

在音频创作的专业舞台上&#xff0c;无线领夹麦克风不仅是声音捕捉的利器&#xff0c;更是创作者表达情感的桥梁。然而&#xff0c;市场上琳琅满目的产品让人眼花缭乱&#xff0c;不少劣质麦克风不仅收音效果大打折扣&#xff0c;还常因信号不稳、噪音干扰而破坏了作品的纯净度…