CAS入门及ABA问题解决

it2023-01-25  60

文章主线

CAS----Unsafe类----CAS底层思想----ABA问题----原子引用更新----ABA问题的规避

什么是CAS?

CAS(CompareAndSwap):比较并交换,是一条CPU并发原语

详述:判断内存某个位置的值是否为预期值,如果是则更新值,否则返回false,这个过程是原子的,底层靠UnSafe类来保证原子性;

并发原语体现在Unsafe中的各个方法,调用Unsafe类中的CAS方法,完全依赖于硬件的功能,通过它实现了原子操作

一般应用:循环判断预期值和主存中值直到相等(更新完成)

什么是原语(操作系统)?

原语:由若干条指令组成,用于完成某个功能的一个过程,并且执行过程必须连续,不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成数据不一致性问题

源码解析

过程描述:先比较(用期望值先和主内存中值进行比较,若结果为true(自己修改之前别人未修改),此时更新值 若比较返回false,则放弃修改 调用过程:用户程序----调用compareAndSet(原子类)----compareAndSwap***(Unsafe类)

AtomicInteger(原子整型类的成员方法实现) public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect,update); } //测试 public static void main(String[] args) { AtomicInteger f=new AtomicInteger(100); System.out.println(f.compareAndSet(100,99)+" "+f.get()); System.out.println(f.compareAndSet(100,101)+" "+f.get()); } 运行结果:true 99 false 99 分析:线程1和线程2同时从主存中拿到f(100),线程1首先将f更新为99,线程2此时发现值已被更新,则放弃更新f
CAS底层原理
public static void main(String[] args) { AtomicInteger f=new AtomicInteger(100); } public class AtomicInteger{//(原子整型类的源码) private volatile int value; public AtomicInteger(int initialValue) { value = initialValue; } public final int getAndAdd(int d) { return Unsafe.getAndAddInt(this, valueOffset, d); } } class UnSafe{//JVM运行时jar包中 public final int getAndAddInt(Object var1,long var2,int var4) { //先存主存中获取值,然后比较并交换(即更新值) int var5; do { var5=this.getIntVolatile(var1,var2); }while(!this.compareAndSwapInt(var1,var2,var5,var5+var4)); return var5; } } 过程分析: var1(this):AtomicInteger对象本身 var2(valueOffset):该对象值的引用地址(内存偏移量) var4(d):需要变动的num var5(期望值):通过var1,var2获取的主存中的值 ----------假设其他线程也正在修改此值--------------- 通过对比期望值与当前与现在主存中的值 若为true,则更新 否则,循环获取期望值,直至更新成功
情景描述

Unsafe类介绍

来源:JVM的rt运行jar包中, 描述:所有方法大部分都是native修饰的,调用操作系统的底层资源执行相应的任务),是CAS的核心类,Java对底层无能为力,需要通过本地native方法来访问,Unsafe相当于一个后门,基于Unsafe类的内部方法可以直接操作特定内存中的数据,因此Java中CAS操作的执行依赖于Unsafe中的方法

CAS是比较并交换,凭什么可以保证原子性?

靠的是Unsafe类,因为Unsafe根据内存地址直接操作数据所以可保证原子性

CAS与synchronized

synchronized:一致性得到保证,但是并发性下降 CAS:一致性、并发性得到保证

不足之处: 1、若CAS失败,则会一直尝试,CPU开销很大 2、当对一个共享变量执行操作时,可使用循环CAS的方式来保证原子操作,有多个共享变量需考虑加锁 3、引出ABA问题

什么是原子类的ABA问题?

ABA问题: CAS算法实现的重要前提是取出内存中某时刻的数据并在当下时刻 比较并交换,那么在这个 时间差 内数据可能会发生 多次变化 情景举例: 线程1从内存中取出A,这时候线程2也从内存中取出A,并且线程2进行了一些操作将值变成了B,然后线程2又将值改回了A,这时候线程1进行CAS操作发现内存中依然是A,然后线程1操作成功

原子更新引用(原子引用类更新值)
//AtomicReference是一个泛型类 public static void main(String[] args) { User u1=new User("张三",21); User u2=new User("李四",22); AtomicReference<User> a=new AtomicReference<>(); a.set(u1); System.out.println(a.compareAndSet(u1, u2)+" "+a.get().toString()); System.out.println(a.compareAndSet(u1, u2)+" "+a.get().toString()); } 结果分析: true [name:李四,age:22] false [name:李四,age:22]
ABA问题的解决?

原子引用+版本号机制(类似于时间戳) jdk实现的带时间戳的原子引用AtomicStampedReference,在每次更新时要判断期望值和版本号两者是否都未发生变化

最新回复(0)