【Java】CAS

Compare And Swap

    • CAS
      • CAS应用
        • 1)实现原子类
        • 2)实现自旋锁
        • CAS的ABA问题
        • 解决 ABA 问题方案

CAS

CAS:是 一条 特殊的 CPU 指令,其所做的工作就是 “比较和交换”。

CAS有3个操作数,内存值V,预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。当多个线程同时尝试使用CAS更新一个变量时,任何时候只有一个线程可以更新成功,若更新失败,线程会重新进入循环再次进行尝试。

CAS的巧妙之处:

  • 之前的线程安全问题是:两个线程从内存中读取同一个值,导致操作覆盖。
  • CAS能确保内存里面的那个值有没有被修改不变才会进行操作,变了的话就会重新读取内存的值。

CAS应用

1)实现原子类

这是Java标准库提供的基于CAS实现的原子类
![[Pasted image 20240115142753.png]]

包底下有如下实现的原子类:
红框标注的是在开发中常用的原子类,其作用:就是对某一变量进行 ++ / -- 操作
【Java】CAS_第1张图片


2)实现自旋锁

CAS实现自旋锁的伪代码:
owner 不为 null 的时候,循环就会一直执行下去,通过不断循环来实现 等待 效果

【Java】CAS_第2张图片


CAS的ABA问题

CAS 在使用的时候,其关键部分就是:判定当前内存中的值是否是和寄存器中的值是一样的;如果是一样的,则进行修改;如果不一样,则不进行修改。

这里的“一样”,本质上是判定当前这个代码执行过程中,是否有其他线程穿插进来将变量给修改

但是存在这种情况:比如初始时数值为0,在执行CAS之前,另一个线程把这个值从 0 改为了 100,而后又将 100 改为了 0。CAS是无法判断这种情况的

一般情况下是不会有问题的。

但能挑出来说,那就是不出意外的要出意外了:

如:在取钱的场景下,初始情况下账户余额有1000元,现要取500元。但在取钱的时候,ATM卡住了,取钱按钮按第一次没反应,又按了一下。
ATM中就创建了两个线程 t1t2 来尝试进行扣款操作,该场景下期望其中一个线程操作成功,另一个操作失败。此处假定使用CAS的方法来扣款:

如图:t1 在执行完第一条指令后,t2 穿插了进来提前执行完整个指令,此时余额已经发生了变化,再轮到 t1 执行第二条指令时,因为余额发生了变化,t2 第二条指令就不会执行,直接跳出。在这种场景下,是没有问题的
【Java】CAS_第3张图片

但如若此时又穿插进来一个 t3 线程,给余额充值了 500,CAS是无法判断这种情况的,那么余额就会莫名少了 500
【Java】CAS_第4张图片


解决 ABA 问题方案
  1. 约定数据变化必须是单向的(只能增加或减少),不可以是双向的(不能既增加又可减少)
  2. 对于本身就必须双向变化的数据,可以给它引入一个版本号(版本号是只能增加不能减少的)

你可能感兴趣的:(java,开发语言)