2023.10.26 关于 CAS 和 ABA 问题

编程入门 行业动态 更新时间:2024-10-20 17:24:24

2023.10.26 关于 CAS 和 <a href=https://www.elefans.com/category/jswz/34/1668520.html style=ABA 问题"/>

2023.10.26 关于 CAS 和 ABA 问题

目录

CAS 操作

执行过程

 CAS 应用场景

实现原子类

实现自旋锁

ABA 问题 


CAS 操作

  • 全称 Compare and swap ,译为 比较并交换

执行过程

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

  • 比较 A 与 V 是否相等
  • 如果比较相等,将 B 写入 V
  • 返回操作是否成功

  • 此处如果 A 和 V 的值不同,则无事发

伪代码

  • 以下是变量自增的伪代码,其不能编译运行,仅表达了一个大概的逻辑思路,方便理解
class AtomicInteger {private int value;public int getAndIncrement() {
//      将内存中的 value 值读取到寄存器 A 中int oldValue = value;while ( CAS(value, oldValue, oldValue+1) != true) {oldValue = value;}return oldValue;}
}
  • oldValue 可以理解为寄存器中的 A 的值
  • 即先把 内存中的 value 读到寄存器 A 中
  • oldValue+1 可以理解为另一个寄存器 B 的值
  • 此处的 while 循环,是为保证仅当寄存器中 A 和 内存中 value 相等,才能进行 将寄存器 B 值写入内存 value 中
  • 如果不同,则返回 false,然后重写读取内存中的 value 并放入寄存器 A 中,继续进入 while 循环
  • 可能导致 寄存器 A 和 value 不相等的原因是:

  • 寄存器 A 读取完 value 后,线程发生了切换,另一线程,对内存 value 值进行了修改,再等到该线程回来时,进行 CAS 判定,此时的 value 和 A 便不相等

注意:

  • 上述 CAS 操作的执行过程,并非是通过一段代码实现的,而是通过 一条 CPU 指令 完成的
  • 即 CAS 操作是 原子的
  • 所以相较于加锁操作,CAS 操作在一定程度上也能够解决线程安全问题

 CAS 应用场景

实现原子类

  • 以下是通过原子类来保证多线修改同一变量的安全性
import java.util.concurrent.atomic.AtomicInteger;public class ThreadDemo29 {public static void main(String[] args) throws InterruptedException {
//        这些都是原子类,就是基于 CAS 实现了 自增,自减操作
//        此时进行这类操作不需要加锁,也是线程安全的AtomicInteger count = new AtomicInteger(0);//        使用原子类,来解决线程安全问题Thread t1 = new Thread(() -> {for (int i = 0; i < 5000; i++) {
//                因为 java 不支持运算符重载,所以只能使用方法来表示自增自减count.getAndIncrement(); // count++// count.incrementAndGet(); // ++count//count.getAndDecrement(); // count--//count.decrementAndGet(); // --count}});Thread t2 = new Thread(() -> {for (int i = 0; i < 5000; i++) {count.getAndIncrement();}});t1.start();t2.start();t1.join();t2.join();System.out.println("count 的值为:" + count.get());}
}

运行结果:


实现自旋锁

自旋锁原理

  • 当锁被其他线程拥有时,另外的线程不会挂起等待,而是会反复询问,看当前锁是否被释放,整个过程为纯用户态的轻量级锁

伪代码

  • 以下是实现自旋锁的伪代码,其不能编译运行,仅表达了一个大概的逻辑思路,方便理解
public class SpinLock {private Thread owner = null;//表示当前锁是谁的public void lock(){ while(!CAS(this.owner, null, Thread.currentThread())){}}public void unlock (){this.owner = null;}
}

图解理解:

  • 监测当前的 owner 是否为 null,如果是 null,就进行交换,也就是把当前线程的引用赋值给 owner
  • 如果赋值成功,此时循环结束,加锁完成
  • 如果当前锁已经被其他线程占用,CAS 便会发现 this.owner 不为 null,CAS 便不会进行赋值,同时返回 false,循环继续执行,进行下次判定

ABA 问题 

  • CAS 操作执行的核心为 检查比较 value 和 oldValue 的值是否相等
  • 如果相等,就视为 value 中途未被其他线程修改,所以进行下一步交换操作是没问题的
  • 但是 这里的相等也可能是 value 值先被修改,再被还原从而导致 value 和 oldValue 值相等
  • 所以 value 和 oldValue 的值相等,并不代表 value 值一定未被修改
  • 所以 CAS 操作 存在 ABA 问题
  • 即  value 值可能最先为 A ,后被其它线程修改为 B ,又被其他线程重新修改回 A,导致 CAS 操作误以为 value 值未被修改,从而进行其交换操作

注意:

  • ABA 问题在大部分情况下,并不会对代码或逻辑造成太大影响
  • 但是还是存在一些极端情况

实例理解

解决方案

  • 针对当前问题,我们可以加入一个版本号
  • 即初始版本号为 1,每次进行修改时,其版本号均 +1
  • 进行 CAS 时,不再以金额为判定基准,而是以版本号为基准
  • 此时版本号要是没变,就一定未发生修改
  • 因为版本号只能增加,不能减少

更多推荐

2023.10.26 关于 CAS 和 ABA 问题

本文发布于:2023-12-03 07:21:04,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1652604.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:ABA   CAS

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!