1. 程式人生 > >無鎖機制下的原子性操作

無鎖機制下的原子性操作

true setname boolean cnblogs 添加 amp 硬件 rfi tom

  通常使用volatile關鍵字修飾字段可以實現多個線程的可見性和讀寫的原子性,但是對於字段的復雜性操作就需要使用synchronize關鍵字來進行,例如:

public class Counter {

    private volatile int count = 0;
    
    public synchronized int getAndIncr() {
        return this.count ++;
    }
    
}

這裏可以看到,對於字段的簡單設置和獲取,volatile可以應付,但是我們想每次獲取後自增加1,這樣的操作就只能交給synchronize來做,這樣做雖然可以但是性能較低,所以我們這裏介紹一種新的無鎖操作CAS。

什麽是CAS(Compare And Swap)

  比較並且交換內存地址的數據,由CPU在指令級別上進行操作的原子性。

CAS包含三個參數:1、變量所在的地址A 2、變量應該的值V1 3、我們需要改的值V2。

如果說變量地址A上的值為V1,就用V2進行復制;如果值不是V1,則什麽都不操作,返回V1的值。

自旋操作:在一個死循環裏不停的進行CAS的操作,直到成功為止。

CAS實現原子操作的三大問題

  1. 當線程想把地址A的值V1改為V3時,當線程取到V1的值後再進行比較時,地址A的值從V1->V2->V1進行了改變,雖然V3可以正常賦值,但是比較的V1值已經不是取出來的V1了。
  2. 循環時間很長的話,CPU的負荷較大。
  3. 對一個變量進行操作可以,同時操作多個共享的變量有些麻煩。

CAS線程安全

  通過硬件層面的阻塞實現原子操作的安全性。

常用的原子操作類

  • 更新基本類型類:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
  • 更新數組類:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
  • 更新引用類:AtomicReference,AtomicReferenceFieldUpdater,AtomicMarkableReference
  • 原子更新字段類:AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicStampedReference
public class AtomicArray {

    static int[] value = new int[]{1, 2};
    static AtomicIntegerArray aia = new AtomicIntegerArray(value);
    
    public static void main(String[] args) {
        aia.set(0, 3);
        System.out.println(aia.get(0));
        System.out.println(value[0]);
    }
}

輸出:

3
1

可以看出AtomicIntegerArray對象將int數組復制了一份,他的改變並沒有影響到原有數組。

public class AtomicRef {

    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;
        }
    }
    
    static AtomicReference<User> userAtomicRef = new AtomicReference<AtomicRef.User>();
    
    public static void main(String[] args) {
        User user = new User("Mark", 25);
        userAtomicRef.set(user);
        User updUser = new User("Mark", 28);
        userAtomicRef.compareAndSet(user, updUser);
        System.out.println(userAtomicRef.get().getName());
        System.out.println(userAtomicRef.get().getAge());
    }
    
}

輸出:

Mark
28

這裏會首先進行比較,然後更新不一樣的字段。

這裏對CAS三大問題的第一個問題進行解決:

AtomicMarkableReference這個類會在數據被修改之後,標記位會由true變為false,判斷這個標記位就可得知。

AtomicStampedReference這個類會在數據上添加時間戳,如果有數據修改時間戳會變掉,從而判斷數據是否被修改。


本文索引關鍵字:

CAS:http://www.cnblogs.com/huanStephen/p/8213678.html#CAS

歡迎大家索引!

無鎖機制下的原子性操作