当前位置: 首页 > ai >正文

Java原子类的实现原理 CAS的使用以及缺陷

在这里插入图片描述

🔍 开发者资源导航 🔍
🏷️ 博客主页: 个人主页
📚 专栏订阅: JavaEE全栈专栏

CAS

CAS(Compare-And-Swap)是一种原子操作,广泛用于并发编程中实现无锁(lock-free)数据结构,它的核心功能是比较并交换。

boolean CAS(address, old_value, new_value) {if (&address == old_value) {&address = new_value;return true;}return false;
}
  1. 读取阶段
    CAS操作会原子性地读取内存中的当前值(称为expectedold_value),同时记录该内存位置的值(这个操作是原子的,不会被中断)。
  2. 比较阶段
    在准备写入新值前,会检查当前内存值&address内部的值,是否仍然等于之前读取的old_value
    • 如果内存值未变(等于old_value),说明内存的值在这个过程中并没有被修改,则进入写入阶段。
    • 如果内存值已变(不等于old_value),说明内存的值在这个过程中被修改过了,则立即返回false(表示失败)。
  3. 写入阶段(仅当比较成功时):
    原子性地将新值(new_value)写入内存,并返回true(表示成功)。
  4. 返回值含义
    • true:内存值未被其他线程修改,且新值写入成功。
    • false:内存值已被其他线程修改,操作失败(此时寄存器中的old_value已过时)。

原子类的实现

既然这个操作是原子性的,那么他对于我们有什么用呢?它最大的作用就是来实现原子类。

上述我们提到过,他可以进行比较和交换操作,同时我们可以用它来判定他是否有被修改过,那我们可以使用这个方式来进行多线程下的增加数值操作:

while ( CAS(value, oldValue, oldValue+1) != true) {oldValue = value;}

在高并发状态下,如果这个值被多个线程同时修改就会产生线程安全问题,最明显的现象就是这个值和我们理想的结果并不一样。

而如果我们使用CAS来进行这个操作,他会根据你的当前内存真实值和之前识别的旧值oldValue是否一致,来判断你的真实值有没有被修改过,而如果是被修改过后的,那么我们的oldvalue也需要进行相应更新,因为我们要交换的值是根据oldvalue来进行判断的。

这样的操作可以保证每次增加的值都是“有效”的,而不会造成线程安全问题。

原子类的使用

原子类的增加机理就是通过上述方式实现的,使用原子类的目的是避免加锁,在Java中提供了一些原子类的封装。

它们被封装在java.util.concurrent.atomic下(非完整版):

原子类描述
AtomicBoolean一个可自动更新的 boolean 值。
AtomicInteger一个可自动更新的 int 值。
AtomicIntegerArray元素可原子更新的 int 数组。
AtomicIntegerFieldUpdater<T>基于反射的工具类,用于原子更新指定类的 volatile int 字段。
AtomicLong一个可自动更新的 long 值。
AtomicLongArray元素可原子更新的 long 数组。
AtomicLongFieldUpdater<T>基于反射的工具类,用于原子更新指定类的 volatile long 字段。

他们的使用方法很简单,例如AtomicInteger增减方法

方法名返回值说明等价操作
getAndIncrement()int原子递增 1,返回旧值i++
getAndDecrement()int原子递减 1,返回旧值i--
getAndAdd(int delta)int原子增加 delta返回旧值-
incrementAndGet()int原子递增 1,返回新值++i
decrementAndGet()int原子递减 1,返回新值--i
addAndGet(int delta)int原子增加 delta返回新值-

基于cas实现自旋锁

基于 CAS 实现更灵活的锁, 获取到更多的控制权.。

伪代码:

public class SpinLock {private Thread owner = null;public void lock(){while(!CAS(this.owner, null, Thread.currentThread())){}}public void unlock (){this.owner = null;}
}
  1. 通过 CAS 看当前锁是否被某个线程持有.
  2. 如果这个锁已经被别的线程持有, 那么就⾃旋等待.
  3. 如果这个锁没有被别的线程持有, 那么就把 owner 设为当前尝试加锁的线程.

CAS缺陷

ABA问题

ABA问题是面试时的常见问题,我们知道CAS的判定机理是判断之前读取到的值和内存里面真实的值进行比较,而如果此时出现一个特殊的情况,他在将A变成B后,又将B变回了A,这时它的判断就会出现误判,而这种情况我们就称之为ABA问题。

虽然他在一般情况下不会对我们造成危害,但是他在特殊情景下会对我们造成危害。

例如:
你的名字叫做小明,今天来到银行取取钱,但是你今天手抖了在取钱的时候多按了一次,还好银行是使用CAS来进行增减(假设!),它在第二次扣款时发现内存值和oldvalue值是不一致的,所以仅扣除了一次。

你不由得感叹CAS的伟大。

第二天,你又来取钱了,你今天又手抖了一次,取了两次100元,原本CAS是可以避免这个情况的,但是今天你的朋友此时此刻“恰巧”给你转了100元,使你的金额又变回了原样,CAS在第二次判断的时候呢发现你的钱没有变化,又给你扣了一次,这时你发现你只取出来100但是实际上少了200。

你此时想对CAS说mmp了。

如何避免?

我们知道ABA问题是由于数值又改变回来导致的,那么我们可以设置一个只增/减的值来当做判断依据,例如设置一个版本号,每一次进行更新的时候+1。

此时你可能想到时间这个单位,他是只增的,但是我们并不建议使用这个,因为时间会出现"润秒"事件,感兴趣的可以自行了解。


感谢各位的观看Thanks♪(・ω・)ノ,如果觉得满意的话留个关注再走吧。

http://www.xdnf.cn/news/8187.html

相关文章:

  • Python unittest
  • Windows逆向工程提升之IMAGE_SECTION_HEADER
  • 解决androidstudio不能识别夜神模拟器的问题
  • 前端 git仓库
  • 蜂鸣器模块
  • Java单例模式终极指南:从原理到防御性编程
  • 在线教育系统源码开发新趋势:白板教学、多端适配与智能组卷机制解读
  • CESM 运行环境搭建实战:Linux 基础、编译体系与 Machine File 配置
  • Java-System工具类深度解析
  • Elabscience CD161/NK1.1 抗体 [PK136]:肿瘤免疫与 NK 细胞活化研究新工具!
  • 灰度矫正算法详解【Halcon】:线性光照不均的处理方法【附代码下载】
  • DAY33
  • CRichEditCtrl 控件实现日志输出
  • 深入解析JMM:Java内存模型与并发编程
  • 2025-05-22 学习记录--Python-函数
  • 使用docker compose部署dify(大模型开发使用平台)
  • DV通配符和OV通配符区别?如何选择?
  • hicFindTADs生成的domains.bed文件解析
  • Android --- CopyOnWriteArrayList 的使用场景及讲解
  • 技术篇-2.5.Matlab应用场景及开发工具安装
  • DDR5和LPDDR5的CA采样时刻对比,含DDR5的1N/2N模式
  • JDK8中的 Stream流式编程用法优化(工具类在文章最后)
  • ollama接口配合chrome插件实现商品标题和描述生成
  • CLIP阅读笔记
  • 亚远景-ASPICE评估中的常见挑战及应对策略
  • 云蝠智能大模型:深度赋能的智能化功能,是怎么做到的?
  • 深入对比分析 Python 中字典和集合 异同
  • 高等数学-曲线积分与曲面积分
  • SpringBoot 对象转换 MapStruct
  • 《函数指针数组:创建与使用指南》