十七、CAS理解

  1. CAS的意思

    CAS 意思就是compareAndSet,比较并设值。

    如果变量的值与期望值相等,则设置新值。

  2. 示例

    public class CASDemo1 {
        public static void main(String[] args) {
            AtomicInteger atomicInteger = new AtomicInteger(100);
    
            //CAS 意思就是compareAndSet
            //如果当前值与期望值相等 则修改
            System.out.println(atomicInteger.compareAndSet(100, 101));
    
            System.out.println(atomicInteger.get());
    
            System.out.println(atomicInteger.compareAndSet(100, 101));
    
            System.out.println(atomicInteger.get());
        }
    }
    

    结果

在这里插入图片描述

  1. ABA问题

    现在有t1,t2两条线程。同时操作var这个变量。t1线程期望的是var==1,则修改var=2。但是同时t2也在操作var,t2线程先执行,它先把var变成了3,然后再把var变成了1。这时候t1执行的时候看到的var虽然是1,但是是已经被修改过的。这就是ABA问题。

    解决这个问题的一个办法就是给var加一个版本号,不管是谁修改了var都对这个版本号加1。其他线程在修改时直接判断版本号。相当于乐观锁。

  2. 原子引用

    带版本号的原子操作,就是一个乐观锁,可以解决ABA问题。

    public class CASDemo2 {
        public static void main(String[] args) {
            //带版本号的原子应用
            AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference(1,1);
    
            new Thread(()->{
                int stamp = atomicStampedReference.getStamp();
                System.out.println(Thread.currentThread().getName()+"->" + stamp);
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                //期望值和期望的版本号都匹配上  则修改值
                boolean res = atomicStampedReference.compareAndSet(1,2,
                        atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
    
                System.out.println(Thread.currentThread().getName()+"->" + res);
    
                boolean res2 = atomicStampedReference.compareAndSet(2,1,
                        atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
    
                System.out.println(Thread.currentThread().getName()+"->" + res);
    
                System.out.println(Thread.currentThread().getName()+"->" + atomicStampedReference.getStamp());
    
            },"a").start();
    
    
            new Thread(()->{
                int stamp = atomicStampedReference.getStamp();
                System.out.println(Thread.currentThread().getName()+"->" + stamp);
    
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                boolean res = atomicStampedReference.compareAndSet(1,6,
                        stamp,stamp+1);
    
                System.out.println(Thread.currentThread().getName()+"->" + res);
    
                System.out.println(Thread.currentThread().getName()+"->" + atomicStampedReference.getStamp());
            },"b").start();
        }
    

    上面的代码中,如果线程a先执行修改操作,就算最后的值被改回了1,但是版本号已经变了,线程b再执行不会修改成功。解决了ABA的问题。

    注意,这里的泛型类Integer如果设置超过了127会出现问题

    在这里插入图片描述

  3. cas的内部是一个自旋锁

在这里插入图片描述

一直比较线程存储区的值和主存中的值,如果相等则修改,否则一直循环

这样会产生一个问题,如果现在有100个线程在调用,那么最后一个线程执行成功时已经自旋了100次,浪费资源。

LongAdder类可以解决这个问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值