欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

CAS 原子操作(Atomic)

程序员文章站 2022-05-03 18:37:06
...

CAS 原子操作: 不可分割的操作

CAS 原理

CAS(Compare And Swap),指令级别保证这是一个原子操作

三个运算符:  一个内存地址V,一个期望的值A,一个新值B

基本思路:如果地址V上的值和期望的值A相等,就给地址V赋给新值B,如果不是,不做任何操作。

循环(死循环,自旋)里不断的进行CAS操作

 

CAS存在的问题

ABA问题:

同时有2个线程获取当前变量值,均为A,当第一个线程对该变量进行2次操作A->B->A.此时对于另外一个线程表现出的现象为此值并未改变,并对该值进行操作。这样就出现了ABA问题。

解决的方法:

给该值添加一个版本号,每一次的操作都会修改版本号,只有修改时版本号与预期版本号相同时,才会操作成功。

CPU消耗

因为CAS操作如果不成功,将会循环进行CAS操作,则对CPU消耗过大

 

JAVA中的Atomic类

CAS 原子操作(Atomic)

 Atomic类的主要公共方法:

boolean compareAndSet(expect, update) 修改值,参数为预期值和更新的值.但此方法只会执行一次,成功返回true,失败返回false,自选更新会使用该方法实现

xxx getAndSet(xxx) 赋值,如果失败,会自选更新

AtomicInteger

public class AtomicIntegerDemo {
    static AtomicInteger count = new AtomicInteger(10);

    public static void main(String[] args) {
        System.out.println(count.getAndIncrement());//(自旋)先获得该值,后+1
        System.out.println(count.incrementAndGet());//(自旋)先+1,在获得该值
        System.out.println(count.get());
    }
}

运行结果:

10
12
12

AtomicReference

public class AtomicReferenceDemo {
    static AtomicReference<User> reference = new AtomicReference<>();

    public static void main(String[] args) {
        User user = new User("lb",18);
        reference.set(user);//赋值
        User user1 = new User("ll",20);
        reference.compareAndSet(user,user1);
        System.out.println(reference.get());
        User user2 = new User("bb",22);
        reference.getAndSet(user2);
        System.out.println(reference.get());
    }


    static class User{
        private String name;
        private int age;

        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
}

AtomicStampedReference

带版本号的Atomic引用类

public class AtomicStampedReferenceDemo {
    //設置初始值,初始版本号
    static AtomicStampedReference<String> reference = new AtomicStampedReference<>("lb", 0);

    public static void main(String[] args) throws InterruptedException {
        String init_name = reference.getReference();
        int init_stamp = reference.getStamp();
        Thread thread1 = new Thread(){
            @Override
            public void run() {
                System.out.println("修改前的name="+init_name+",版本:"+init_stamp
                        +".修改的结果:"+reference.compareAndSet(init_name,init_name+"_java",init_stamp,init_stamp+1));

            }
        };

        Thread thread2 = new Thread(){
            @Override
            public void run() {
                String name = reference.getReference();
                System.out.println("修改前的name="+name+",版本:"+init_stamp
                        +".修改的结果:"+reference.compareAndSet(init_name,name+"_C",init_stamp,init_stamp+1));

            }
        };
        thread1.start();
        thread1.join();
        thread2.start();
        thread2.join();
        System.out.println("最终: name="+reference.getReference()+",版本:"+reference.getStamp());
    }
}

运行结果:

修改前的name=lb,版本:0.修改的结果:true
修改前的name=lb_java,版本:0.修改的结果:false
最终: name=lb_java,版本:1

注意:

AtomicMarkableReference,boolean 有没有动过

AtomicStampedReference  动过几次

AtomicIntegerArray

 

public class AtomicIntegerArrayDemo {
    static int[] arr = new int[]{2,1};
    static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arr);

    public static void main(String[] args) {
        System.out.println(atomicIntegerArray.get(0));//参数为下标
        atomicIntegerArray.set(0,3);
        System.out.println(atomicIntegerArray.get(0));
        System.out.println(arr[0]);
    }
}

运行结果:修改的值是AtomicIntegerArray中存储的副本,而不是数组本身

2
3
2