CAS
Posted 鸭猪是的念来过倒
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CAS相关的知识,希望对你有一定的参考价值。
1:CAS是什么?
比较并交换 (compareAndSwap),是一条CPU并发原语,是乐观锁技术的一种实现,当多个线程尝试使用CAS同时修改同一个变量时,只有其中一个线程能修改变量的值,而其它线程都失败,失败的线程并不会被挂起或者结束,
而是继续尝试(循环比较)修改,直到修改成功
源码一览:
public final int getAndAddInt(Object var1, long var2, int var4) int var5; do
//获取数据 var5 = this.getIntVolatile(var1, var2);
//比较数据 v1和v2找到值 和v5做比较 while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5;
v1是当前对象,v2是内存地址偏移量,通过v1和v2可以查找到一个精确的数据并拷贝到自己线程工作区域内,然后根据这个数据和v5进行比较,只要是一致的,那么执行v5 + v4
当线程被切换过之后,那么此时,v5还是v5,但是v1和v2的值却不一定等于v5了,因为已经切换过其他线程,其他线程可能对这个v1和v2已经做了修改,如果做了修改,那么将继续循环比较,如此往复,直到成功
优点:
1:不加锁,通过unSafe类解决数据一致性问题
2:直接操作主内存 不存在数据不一致问题
3:通过自旋锁方式,对数据进行比较和交换
缺点:
1:自旋锁对cpu开销特别大
2:只能保证一个共享变量的原子操作
3:导致ABA问题
ABA问题:
两个线程通过cas操作同一个变量,当T1线程和T2线程都获得主内存X的时候,T1的期望值是A想要修改成B,但是T2线程快速的将主内存A修改为任意值,然后又将任意值修改回A,此时T1再次获取主内存 值的时候发现 还是A就做了修改。表面上程序是正常运行的,但是这个过程中这个值是存在多变的,不保证这个过程没有问题!
代码示例:
import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; public class test static AtomicReference<Integer> atomicReference = new AtomicReference<>(100); public static void main(String[] args) new Thread(() -> atomicReference.compareAndSet(100,101); atomicReference.compareAndSet(101,100); ,"t1").start(); new Thread(() -> try TimeUnit.SECONDS.sleep(2); //在本次修改之前 其实数据已经被修改过,但是此处只做第一次和最后一次的比较,发现100=100,所以就直接继续修改完成 System.out.println(atomicReference.compareAndSet(100, 2000) + " \\t getVaue:" + atomicReference.get()); catch (Exception d) d.printStackTrace(); ,"t2").start();
解决ABA问题:
通过 AtomicStampedReference来添加版号作为修改的依据,如下代码,只要ABA发生,数据则修改失败!
import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicStampedReference; public class test static AtomicReference<Integer> atomicReference = new AtomicReference<>(100); static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1); public static void main(String[] args) new Thread(() -> atomicReference.compareAndSet(100,101); atomicReference.compareAndSet(101,100); ,"t1").start(); new Thread(() -> try TimeUnit.SECONDS.sleep(2); System.out.println(atomicReference.compareAndSet(100, 2000) + " \\t getVaue:" + atomicReference.get()); catch (Exception d) d.printStackTrace(); ,"t2").start(); //处理ABA问题 new Thread(() -> try TimeUnit.SECONDS.sleep(3); catch (Exception e) e.printStackTrace(); atomicStampedReference.compareAndSet(100,200,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); atomicStampedReference.compareAndSet(200,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); ,"t3").start(); new Thread(() -> int stamp = atomicStampedReference.getStamp(); try TimeUnit.SECONDS.sleep(3); catch (Exception e) e.printStackTrace(); boolean b = atomicStampedReference.compareAndSet(100, 500, stamp, stamp + 1); System.out.println(Thread.currentThread().getName() + " \\t 版本号:" + stamp + ",当前版本号: "+atomicStampedReference.getStamp()+"修改结果:" + b + ",实际值是:" + atomicStampedReference.getReference()); ,"t4").start();
以上是关于CAS的主要内容,如果未能解决你的问题,请参考以下文章
单点登录CAS使用记:使用maven的overlay实现无侵入的改造CAS