CAS 原子操作(Atomic)

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類

 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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章