JAVAEE之多线程进阶(2)_ CAS概念、实现原理、ABA问题及解决方案

前言

 在并发编程时,常常会出现线程安全问题,那么如何保证原子性呢?常用的方法就是加锁。在Java语言中可以使用 Synchronized和CAS实现加锁效果。
 Synchronized关键字保证同步的,这会导致有锁,但是锁机制存在以下问题:

  1. 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。

  2. 一个线程持有锁会导致其它所有需要此锁的线程挂起。

  3. 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。

而volatile是不错的机制,但是volatile不能保证原子性。因此对于同步最终还是要回到锁机制上来。
 独占锁是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁用到的机制就是CAS,Compare and Swap


一、什么是CAS?

1.1 概念:

 CAS全称Compare and swap,字面意思:”比较并交换“,它是一条 CPU 并发原语,用于判断内存中某个值是否为预期值,如果是则更改为新的值,这个过程是原子的。

1.2 实现步骤

具体步骤如下所示:

  1. 一个初始值变量V,值为5;一开始先读取V实际内存中的值赋值给E。
  2. 比如我们需要给最原始的V+1操作,那么此时用E+1来进行操作(这是防止V在其他线程已经被改变),这样完成了U=E+1的操作。
  3. 判断E和V的值是否一致,如果一致则证明在以上操作过程中V没有被其他线程改变则将U的值赋值给V,如果不一致那V就被其他改变了,这样给U的+1操作就不成立,返回当前的V。
    在这里插入图片描述

1.3 CAS 伪代码

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

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

1.4 实现自旋锁

基于 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中的问题

2.1 ABA问题

 ABA 是 CAS 操作的一个经典问题,假设有一个变量初始值为 A,修改为 B,然后又修改为 A,这个变量实际被修改过了,但是 CAS 操作可能无法感知到。
 假设存在两个线程 t1 和 t2,有一个共享变量 num,初始值为 A。接下来, 线程 t1 想使用 CAS 把 num 值改成 Z, 那么就需要

  1. 先读取 num 的值, 记录到 oldNum 变量中;
  2. 使用 CAS 判定当前 num 的值是否为 A, 如果为 A, 就修改成 Z.

但是, 在 t1 执行这两个操作之间,t2 线程可能把 num 的值从 A 改成了 B,又从 B 改成了 A。
在这里插入图片描述

 如果是整形还好,不会影响最终结果,但如果是对象的引用类型包含了多个变量,引用没有变实际上包含的变量已经被修改,这就会造成大问题

2.2 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 问题导致的结果。

2.3 ABA解决方案

给要修改的值,引入版本号,在 CAS 比较数据当前值和旧值的同时,也要比较版本号是否符合预期。

  1. CAS 操作在读取旧值的同时,也要读取版本号。
  2. 真正修改的时候,
    如果当前版本号和读到的版本号相同, 则修改数据, 并把版本号 +1;
    如果当前版本号高于读到的版本号,就操作失败(认为数据已经被修改过了)。

我们依然举一个银行取款的例子:

为了解决 ABA 问题, 给余额搭配一个版本号, 初始设为 1:

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

总结

讲解下自己理解的 CAS 机制

 全称 Compare and swap, 即 “比较并交换”, 相当于通过一个原子的操作, 同时完成 “读取内存, 比较是否相等, 修改内存” 这三个步骤. 本质上需要 CPU 指令的支撑。

ABA问题如何解决

 给要修改的数据引入版本号, 在 CAS 比较数据当前值和旧值的同时,也要比较版本号是否符合预期。如果发现当前版本号和之前读到的版本号一致,就真正执行修改操作,并让版本号自增;如果发现当前版本号比之前读到的版本号大, 就认为操作失败。

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

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

相关文章

douyin-vue:使用Vue3、Pinia和Vite5打造高度还原的抖音仿制项目

一:引言 在前端技术日新月异的今天,Vue.js作为一款流行的前端框架,不断吸引着开发者的目光。最近,GitHub上出现了一个备受瞩目的项目——douyin-vue,这是一个基于Vue3、Pinia和Vite5的移动端短视频项目,旨…

通过ESP32芯片模组实现产品智能化升级,启明云端乐鑫代理商

随着科技的不断进步,物联网(IoT)已经渗透到我们生活的方方面面,成为现代生活不可或缺的一部分。在这场智能化革命中,乐鑫科技以其创新的ESP32芯片模组,为智能家居和智能设备的发展注入了新的活力。作为乐鑫…

msi安装mysql8 启动失败,提示只有在任务处于完成状态(RanToCompletion、Faulted 或 Canceled)时才能释放它。

解决方案: 1.打开服务,找到安装的mysql 2. 右击,打开属性,进入【登录】选项卡,选择本地系统账户。 3. 点击确定-->应用 4.服务中选择开始服务 5.服务启动成功后,在安装步骤中继续点击执行

Post Microsoft Build and AI Day 上海开发者日

点击蓝字 关注我们 编辑:Alan Wang 排版:Rani Sun 这个六一怎么过?来微软 Reactor,一起过儿童节吧! 6月1日,Microsoft Azure & Microsoft Reactor 面向大小朋友特别推出六一特辑,「Post Mic…

开源进销存系统

推荐一款开源的进销存系统 项目地址:进销存系统 仓库管理系统 SAAS进销存 进销存ERP: 进销存系统 仓库管理系统 SAAS进销存 进销存ERPhttps://gitee.com/flyemu/jxc.git 主要功能模块 销售 采购 库存 资料 设置 支持saas多租户,100%开源可二开 …

iPhone用户推荐使用的藏汉翻译小助手:藏汉翻译通小程序,支持藏文OCR识别文字提取,卫藏语、安多语、康巴语学习背单词!

网上冲浪时,遇到不会的汉语词汇,可以复制到藏汉翻译通小程序中进行翻译。如果不会拼音,可以使用图片识别功能扫一扫文字,即可OCR识别提取文字。 此外,藏汉翻译通小程序现在还支持背单词和会话速成课程,支持…

Pytorch-Reduction Ops

文章目录 前言1.torch.argmax()2.torch.argmin()3.torch.amax()4.torch.amin()5.torch.all()6.torch.any()7.torch.max()8.torch.dist()9.torch.logsumexp()10.torch.mean()11.torch.norm()12.torch.nansum()13.torch.prod()14.torch.cumsum()15.torch.cumprod() 前言 1.torch.…

node环境问题(无法加载文件D:\Software\Node.js\node_global\vue.ps1,因为在此系统上禁止运行脚本。)

问题:npm安装lerna显示安装成功,但是lerna -v的时候报错 解决步骤: 1、输入:Get-ExecutionPolicy 2、输入:Set-ExecutionPolicy -Scope CurrentUser(有选项的选Y) 3、输入:RemoteSi…

【记录】打印|无需排版,生成证件照打印PDF,打印在任意尺寸的纸上(简单无损!)

以前我打印证件照的时候,我总是在网上找在线证件照转换或者别的什么。但是我今天突然就琢磨了一下,用 PDF 打印应该也可以直接打印出来,然后就琢磨出来了,这么一条路大家可以参考一下。我觉得比在线转换成一张 a4 纸要方便的多&am…

深入探索MySQL SELECT查询:从基础到高级,解锁数据宝藏的密钥

系列文章目录 更新ing... MySQL操作全攻略:库、表、数据、事务全面指南深入探索MySQL SELECT查询:从基础到高级,解锁数据宝藏的密钥MySQL SELECT查询实战:练习题精选,提升你的数据库查询技能PyMySQL:连接P…

解决 x-content-sha256 no match 错误,对 S3CrtAsyncHttpClient 修改

一、CRT修改核心逻辑: 找到 software.amazon.awssdk.services.s3.internal.crt 包下 S3CrtAsyncHttpClient 按照逻辑需要对 GET请求进行适配 signingConfig.setSignedBodyValue(AwsSigningConfig.AwsSignedBodyValue.EMPTY_SHA256); if("GET".equals(asyncRequ…

orin部署tensorrt、cuda、cudnn、pytorch、onnx

绝大部分参考https://blog.csdn.net/qq_41336087/article/details/129661850 非orin可以参考https://blog.csdn.net/JineD/article/details/131201121 报错显卡驱动安装535没法安装、原始是和l4t-cuda的部分文件冲突 Options marked [*] produce a lot of output - pipe it t…

数据结构(一)顺序表

目录 一、概念(一)数据结构的三元素1. 逻辑结构(1)线性结构(2)非线性结构 2. 存储结构(1)顺序存储(2)链式存储(3)索引存储 3. 运算 &a…

Linux下Git的基本使用

认识Git 先基于Windows下的git操作,熟悉了git的基本概念和使用,直接参考这几篇文章: Git概述、安装与本地仓库的基本操作-CSDN博客 Git本地仓库与远程仓库的交互-CSDN博客 GtiHub远程仓库之间的交互-CSDN博客 Git仓库的分支操作-CSDN博客 仓库…

服装服饰商城小程序的作用是什么

要说服装商家,那数量是非常多,厂家/经销门店/小摊/无货源等,线上线下同行竞争激烈,虽然用户群体广涵盖每个人,但每个商家肯定都希望更多客户被自己转化,渠道运营方案营销环境等不可少。 以年轻人为主的消费…

国产PS插件新选择;StartAI平替中的佼佼者!

前言 在设计的世界里,每一个细节都至关重要。设计师们常常面临时间紧迫、创意受限、工具复杂等挑战。Photoshop虽强大,但繁琐的操作和高昂的成本往往令人望而却步。今天我就为大家介绍一款PSAI插件——StartAI,一款专为Photoshop设计的国产A…

【Linux终端探险】:从入门到熟练,玩转基础命令的秘密(一)

文章目录 🚀Linux基础命令⭐1. 查看目录命令💥2. 切换目录👊3. 创建目录❤️4. 删除目录/文件🚲5. 修改目录/文件🌈6. 拷贝目录/文件 🚀Linux基础命令 ⭐1. 查看目录命令 在Linux中,查看目录的…

C语言⾼位优先与低位优先的不同之处是什么?

一、问题 C语⾔的最⼤特⾊就是可移植性好。根据机器类型的不同,⾼位优先与低位优先也不同。那么,最好的可移植的 C 程序应该同时适⽤这两种类型的计算机。下⾯了解⼀下⾼位优先与低位优先的不同之处。 二、解答 所谓的⾼位优先,就是最低的地…

AUS GLOBAL 荣获 Brokersview 颁奖盛典多项殊荣

2024年1月31日在迪拜 Sheikh Zayed Rd - Trade Centre - Trade Centre 1 举行的 Brokersview 颁奖盛典上,AUS GLOBAL(澳洲环球)再次展现了其在金融行业的卓越实力,并荣获多项殊荣。 AUS GLOBAL 作为一家全球领先的金融服务提供商…

一个交易者的自白:念念不忘的交易,10个日内9个亏

一、新手: 面对爆仓,我像个白痴 我是在2012年开始接触的,这些年里我尝到了残酷失败的滋味,更品尝过胜利带来的喜悦。刚刚接触时很自信,总想着自己有一天一定会变成千万富翁的,用杠杆获取暴利。 在我首次爆仓的时候,我的…