java Atomic 原子操作 juc

原子操作類

原子性這個概念,在多線程編程裏是一個老生常談的問題。

所謂的原子性表示一個或者多個操作,要麼全部執行完,要麼一個也不執行。不能出現成功一部分失敗一部分的情

況。

在多線程中,如果多個線程同時更新一個共享變量,可能

會得到一個意料之外的值。比如 i=1 。A 線程更新 i+1 、

B 線程也更新 i+1。

通過兩個線程並行操作之後可能 i 的值不等於 3。而可能等

於 2。因爲 A 和 B 在更新變量 i 的時候拿到的 i 可能都是 1

這就是一個典型的原子性問題

前面幾節課我們講過,多線程裏面,要實現原子性,有幾

種方法,其中一種就是加 Synchronized 同步鎖。

而從 JDK1.5 開始,在 J.U.C 包中提供了 Atomic 包,提供了

對於常用數據結構的原子操作。它提供了簡單、高效、以

及線程安全的更新一個變量的方式

J.U.C 中的原子操作類

由於變量類型的關係,在 J.U.C 中提供了 12 個原子操作的

類。這 12 個類可以分爲四大類

1. 原子更新基本類型

AtomicBoolean、AtomicInteger、AtomicLong

2. 原子更新數組AtomicIntegerArray 、 AtomicLongArray 、

AtomicReferenceArray

3. 原子更新引用

AtomicReference 、 AtomicReferenceFieldUpdater 、

AtomicMarkableReference(更新帶有標記位的引用類

型)

4. 原子更新字段

AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、

AtomicStampedReference

A、原子更新基本類型

以AtomicInteger爲例

public class AtomicIntegerTest {
        static AtomicInteger  ai = new AtomicInteger(2);
    public static void main(String[] args) {
        ai.compareAndSet(2,3);
        System.out.println(ai.get());
        System.out.println(ai.getAndSet(8));
        System.out.println(ai.get());
        System.out.println(ai.getAndIncrement());
        System.out.println(ai.get());
    }
}

輸出結果

3
3
8
8
9

源碼是基於JDK1.8
public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);//調用unsafe的getAddInt方法
}

 

 

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);//獲取當前對象在內存偏移地址上的值
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));//嘗試修改,直到成功

    return var5;
}

 

可以看到unsafe類只提供類,三個基本的cas方法

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

,我們發現Unsafe只提供了3CAS方法:compareAndSwapObjectcompare-

AndSwapIntcompareAndSwapLong,再看AtomicBoolean發現它是先把Boolean轉換成整

型,再使用compareAndSwapIntCAS,所以原子更新charfloatdouble量也可以用

的思路來實現

 

B、原子更新數組類型

AtomicLongArray爲例

public class AtomicLongArrayTest {
    static long[] value  = new long[]{1,4,5,6,7};
    static AtomicLongArray al = new AtomicLongArray(value);
    public static void main(String[] args) {
        System.out.println(al.getAndSet(3,999));
        System.out.println(al.get(3));
    }
}

輸出結果

6
999
 

源碼簡單分析,可以看到最終也是 調用unsafe類的方法

public final long getAndSet(int i, long newValue) {
    return unsafe.getAndSetLong(array, checkedByteOffset(i), newValue);
}
public final long getAndSetLong(Object var1, long var2, long var4) {
    long var6;
    do {
        var6 = this.getLongVolatile(var1, var2);
    } while(!this.compareAndSwapLong(var1, var2, var6, var4));//和AtomicInteger類似

    return var6;
}

C、原子更新引用類型

public class AtomicReferenceTest {

    public static AtomicReference<User> atomicReference = new AtomicReference<User>();


    public static void main(String[] args) {
        User user = new User("sun",99);
        atomicReference.set(user);
        atomicReference.compareAndSet(user,new User("xxsun",100));
        System.out.println("name:"+atomicReference.get().getName()+",age:"+atomicReference.get().getAge());
    }
    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;
        }
    }
}

輸出結果

name:xxsun,age:100

看下源碼

public final boolean compareAndSet(V expect, V update) {
    return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

如果只是需要改變對象內的部分屬性的值怎麼辦,接着往下

D、原子更新字段類

如果需原子地更新某個裏的某個字段,就需要使用原子更新字段Atomic包提供

了以下3類進行原子字段更新。

·AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。

·AtomicLongFieldUpdater:原子更新整型字段的更新器。

·AtomicStampedReference:原子更新有版本號的引用型。該類將整數與引用關

來,可用於原子的更新數據和數據的版本號,可以解決使用CAS行原子更新可能出

ABA問題

要想原子地更新字段需要兩步。第一步,因原子更新字段都是抽象,每次使用的

候必使用靜方法newUpdater()建一個更新器,並且需要置想要更新的和屬性。第

二步,更新的字段(屬性)必使用public volatile符。

例

public class AtomicStampedRefrenceTest {


        static volatile User user = new User("sun",99,110);
    public static void main(String[] args) {
     AtomicStampedReference<User> atomicReference =  new AtomicStampedReference<User>(user,2);
        atomicReference.set(user,2);
        atomicReference.compareAndSet(user,new User("sun...",3,130),2,10);//通過每次修改stamp來避免ABA問題的出現
        System.out.println("name:"+atomicReference.getReference().getName()+"age:"+atomicReference.getReference().getAge()+",weight:"+atomicReference.getReference().getWeight());
    }
    static class User{
        private  String name;
        private volatile int age;
        private volatile  int weight;

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

        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;
        }

        public int getWeight() {
            return weight;
        }

        public void setWeight(int weight) {
            this.weight = weight;
        }
    }

輸出結果

name:sun...age:3,weight:130
 

 

源碼

public boolean compareAndSet(V   expectedReference,
                             V   newReference,
                             int expectedStamp,
                             int newStamp) {
    Pair<V> current = pair;
    return
        expectedReference == current.reference &&
        expectedStamp == current.stamp &&
        ((newReference == current.reference &&
          newStamp == current.stamp) ||
         casPair(current, Pair.of(newReference, newStamp)));
}
private boolean casPair(Pair<V> cmp, Pair<V> val) {
    return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}

 

總結:atomic原子操作類最終都是調用Unsafe類的幾個方法,相對比較簡單

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5); public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

 

 

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