CAS是什么?ABA是什么?原子引用?

CAS即compareAndSwap,比较并交换

如原子型整数,AtomicInterger中就有一个CAS方法(CAS在底层,此Set的S非彼S,不要误解,往下看!):

compareAndSet(expect,update),输入一个期望值expect,和一个更新值update,

如果atomicInterger=expect,则重新赋值为update,并返回true。

如果值已经被改动,则返回false,不进行操作。

底层是使用Unsafe类,jre的rt.jar中sum/misc目录下。
在这里插入图片描述

里面的方法全部都是用native修饰的本地方法,可以直接使用系统资源进行操作,是最高级的,不可被打断,所以保证了其原子性!

而这个AtomiciInterger中的数据就是用volatile修饰的,保证了其可见性!

所以通过CAS+volatile这种方法也可以保证JMM模型的三大特性!

但是CAS也是有缺点的,还一下子就是三个!

CAS三大缺点

1.耗时长,消耗系统资源

因为是CAS需要进行比较,在一些操作中,如AtomicInterger中的getAndIncrement()方法

其源码调用的就是Unsafe的getAndAddInt()方法

而再里面就是用的compareAndSwap这种真正的CAS操作!

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;
    }

举个例子解释一下

 AtomicInteger atomicInteger = new AtomicInteger();//默认值为0

如拿到的是atomicInterger的值=5,现在要继续加1

var1就是 atomicInterger这个对象,var2就是偏移量地址,

unsafe就是通过这个偏移量地址去内存找出它的值为5;

用var5来接收从内存读到的值;

var4就是我们需要加的值,这里是+1;

开始进行CAS,期望值为var5,更新值为var5+var4;

本地方法开始操作,可以当做打开了一个绝对领域,这里只有我在操作,无法被打断或操作!

在里面一样通过var1和var2获得一个值用var3来接收

当var3==var5时,我们就将值改为var4,输入到内存中!

但前面在外面获取值时,还没进入绝对领域的情况下,别人已经改完了,内存里的值已经是6了!

因为用volatile修饰过,所以进入领域中就可以看到已经变成6了!

那么比较的结果就为false,只能退出领域重新获取值再进入!

所以这就是为什么要写一个dowhile循环,这样就有可能进入自旋状态,一直判断一直判断!

就很浪费资源和时间!但也是因为这样才保证了其原子性!不像普通的线程不管三七二十一,一顿乱改!一点规矩都不讲!

2.只能有一个共享变量

原子数据类型,一次只能使一个共享变量拥有原子性!

3.ABA

什么是ABA?

举个例子!

有线程1和线程2同时从内存中读到了值为A,

这个时候线程1在对A进行操作时,线程2已经将A改成B并放入到内存中,

但是线程3这个时候跑过来把你B又改成了A再放回去。

此时线程1才到绝对领域中进行CAS,比较了一下发现,值没错,就是A,我开始操作巴拉巴拉!

虽然线程1使用CAS保证了原子性,但中间被人偷偷改过都不知道,所以可能造成一定概率的错误!

public class AtomicDemo {
    public static void main(String[] args) {
        AtomicReference<String> atomicStr = new AtomicReference<String>("A");

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //目标值,更新值,目标版本,更新版本
            atomicStr.compareAndSet("A","C");
            System.out.println(Thread.currentThread().getName()+"\t "+atomicStr.get());
        },"1").start();


        new Thread(()->{
            //目标值,更新值,目标版本,更新版本
            atomicStr.compareAndSet("A","B");
            atomicStr.compareAndSet("B","A");
        },"2").start();


    }
}

输入结果如下

1	 C

怎么避免ABA?

最简单的方法就是打个标记,如时间戳版本号之类的。

原子类不止有基本的数据类型,还有原子引用!可以将我们自定义的类原子化!

原子引用AtomicReference
        AtomicReference<Integer> integerAtomicReference = new AtomicReference<>();

使用泛型的方式包裹任意类,可以是基本数据类型,也可以是我们的自定义类。

不过要解决ABA的问题,光用这个不够,这个类只能保证原子性,我们还要为这个类打上记号stamped这个类就叫做AtomicStampedReference

/**
 * Creates a new {@code AtomicStampedReference} with the given
 * initial values.
 *
 * @param initialRef the initial reference
 * @param initialStamp the initial stamp
 */
public AtomicStampedReference(V initialRef, int initialStamp) {
    pair = Pair.of(initialRef, initialStamp);
}

其构造方法不仅需要一个保护对象,还要打入一个int类型的记号,我们可以简单理解为版本号!

public class AtomicDemo {
    public static void main(String[] args) {
        AtomicStampedReference<String> atomicStr = new AtomicStampedReference<>("A", 1);

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //目标值,更新值,目标版本,更新版本
            atomicStr.compareAndSet("A","C",1,2);
            System.out.println(Thread.currentThread().getName()+"\t "+atomicStr.getReference()+"\t"+atomicStr.getStamp());
        },"1").start();


        new Thread(()->{
            //目标值,更新值,目标版本,更新版本
            atomicStr.compareAndSet("A","B",1,2);
            atomicStr.compareAndSet("B","A",2,3);
        },"2").start();


    }
}

1	 A	3

当我们再遇到ABA问题时,就可以先比较其版本号,值和版本号都匹配成功,我们才进行修改,否则不操作!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值