理解CAS


什么是CAS


public class CASDemo {
    //CAS compareAndSet:比较并交换
    public static void main(String[] args) {
        AtomicInteger atomicInteger=new AtomicInteger(2020);

        //期望,更新
        /*public final boolean compareAndSet(int expect, int update) {
            return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
        }*/
        //如果期望的值达到了,那么就更新,否则就不更新 CAS cpu的并发原因
        System.out.println(atomicInteger.compareAndSet(2020, 2021));

        System.out.println(atomicInteger.get());

        atomicInteger.getAndIncrement();

        System.out.println(atomicInteger.compareAndSet(2020, 2022));

        System.out.println(atomicInteger.get());
    }
}

CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作(否则do while 即自旋锁)

缺点:

  • 底层是自旋锁do while,循环是耗时
  • 一次性只能保证一个共享变量的原子性
  • ABA问题

Unsafe

ABA问题 狸猫换太子

public class CASDemo {
    //CAS compareAndSet:比较并交换
    public static void main(String[] args) {
        AtomicInteger atomicInteger=new AtomicInteger(2020);

        //对于我们平时写的sql来说 一般使用乐观锁来解决
        
        //期望,更新
        /*public final boolean compareAndSet(int expect, int update) {
            return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
        }*/
        //如果期望的值达到了,那么就更新,否则就不更新 CAS cpu的并发原因

        //==================捣乱线程==================//
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.compareAndSet(2021, 2020));

        System.out.println(atomicInteger.get());

        //atomicInteger.getAndIncrement();
        //==================期望线程==================//
        System.out.println(atomicInteger.compareAndSet(2020, 2021));

        System.out.println(atomicInteger.get());
    }
}

解决ABA问题- 原子引用

带版本号的原子操作

public class CASDemo {
    //CAS compareAndSet:比较并交换
    public static void main(String[] args) {

        //AtomicInteger atomicInteger=new AtomicInteger(2020);
        //int Integer包装类有一个对象缓存机制 默认是-128~127,推荐使用静态工厂方法valueof获取对象实例,而不是new
        //如果范围超过 -128~127 那么new一定会创建新的对象分配新的内存空间,这时比较就会比较的内存地址而不是值,所以会变为false
        AtomicStampedReference atomicInteger=new AtomicStampedReference<>(1,1);
        //注意:如果泛型是一个包装类,注意对象的引用问题
        new Thread(()->{
            int stamp = atomicInteger.getStamp(); //获得版本号 最初的期望版本号
            System.out.println("a1=>"+atomicInteger.getStamp());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //version+1
            System.out.println(atomicInteger.compareAndSet(1, 2, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
            System.out.println("a2=>"+atomicInteger.getStamp());
            System.out.println(atomicInteger.compareAndSet(2, 1, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
            System.out.println("a3 =>"+atomicInteger.getStamp());
        },"A").start();
        new Thread(()->{
            int stamp = atomicInteger.getStamp(); //获得版本号
            System.out.println("b1=>"+atomicInteger.getStamp());
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicInteger.compareAndSet(1, 6, stamp, atomicInteger.getStamp() + 1));
            System.out.println("b2=>"+atomicInteger.getStamp());
        },"B").start();

    }
}

注意点