CAS Compare and Swap 比较后交换
序
since: 2019年8月25日 22:44
auth: Hadi
update:2021年5月16日 22:55
CAS :Compare and Swap
字面意思就是比较后进行交换。在多线程中,经常有多个线程对一个公用变量进行值的更改和比较。那么在进行变量更改的时候,就必须考虑一致性的问题。
产生原因
当T1线程与T2线程在同时进行更改一个数据时,就会产生竞争的现象。如下图:
两个线程同时对一个变量进行++的操作,那么可能会导致取数时为1,进行各自local Memory的计算后写回主内存的时候都写为2。
那么最终得到的结果就为2,但真正的结果应该为3.
解决办法
当T1写入Main Memory的时候,进行预先判断,判断写入之前的值是否是我操作之前的值,如果是那么就进行写入。当T2进行写入的时候,采用同样的操作,却发现并不是我之前操作的值,那么就会重新read MainMemory中的这个值,并重新进行整个流程。
使用场景
- 自旋锁
- Unsafe类
在atomic类中,比如 atomicInteger.getAndIncrement(); 自增,没有加syn也没有出错
其直接调用了unsafe类的getAndAddInt方法(这个类, 内存地址, 自增值),在atomic类中这样构造:
注意 unsafe使用Unsafe.getUnsafe()获得
valueOffset 是内存地址(偏移量)
而unsafe是 sun.misc 下的。
是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地native 方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定的内存数据。
unsafe的内部方法可以向C的指针一样直接操作内存。
Native 修饰的方法,直接调用操作系统底层资源执行相应任务 某个.dll
那如果比较后,其他线程进行了更改怎么办?还是会导致写覆盖
CAS的全称为 CompareAndSwap 是一CPU并发原语
他的功能是判断内存某个位置的值,是否为预期值,如果是则更改为新的值,这个过程是原子的。
体现在JAVA语言中就是sun.misc.Unsafe类中的各个方法。调用Unsafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。再次强调,CAS是一种系统原语,是由若干条指令组成的,用于完成某个功能的一个过程。
并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是CAS是一条CPU原子指令,不会造成所谓的数据不一致问题。
-
AtomicInteger.getAndIncrement();
-
unSafe
-
compateAndSwap 其实应该是 对象,地址, 以前的值, 想要更改的值
为什么用CAS 不用syn
syn同一时间只允许一个线程访问。但CAS其实是多个线程允许一起访问的,且不会阻塞。
CAS的缺点
循环之间长,开销很大。
只能保证一个共享变量的原子操作。
引出来ABA问题。
ABA问题
可能当我比较的时候确实是原来的值,但其实在中途已经被更改过多次了,并不是原来的版本;在某些场景下必须进行版本的控制,则可以在此之上进行版本相关的控制。