Java併發之原子操做類彙總

當程序更新一個變量時,若是是多線程同時更新這個變量,可能獲得的結果與指望值不一樣。好比:有一個變量i,A線程執行i+1,B線程也執行i+1,通過兩個線程的操做後,變量i的值可能不是指望的3,而是2。這是由於,可能在A線程和B線程執行的時候拿到的i的值都是1,這就是線程不安全的更新操做,一般咱們會使用synchronized來解決這個問題,synchronized能保證多線程不會同時更新變量i.html

從java1.5開始,jdk提供了java.util.concurrent.atomic包,這個包中的原子操做類,提供了一種用法簡單,性能高效,線程安全的更新一個變量的方式。java

atomic包裏面一共提供了13個類,分爲4種類型,分別是:原子更新基本類型原子更新數組原子更新引用原子更新屬性,這13個類都是使用Unsafe實現的包裝類。數組

1、原子更新基本類型

atomic提供了3個類用於原子更新基本類型:分別是AtomicInteger原子更新整形,AtomicLong原子更新長整形,AtomicBoolean原子更新bool值。因爲這三個類提供的方法幾乎是同樣的,所以本節以AtomicInteger爲例進行說明。安全

  AtomicInteger的經常使用方法有:多線程

    1.int addAndGet(int delta):以原子的方式將輸入的值與實例中的值相加,並把結果返回性能

    2.boolean compareAndSet(int expect, int update):若是輸入值等於預期值,以原子的方式將該值設置爲輸入的值this

    3.final int getAndIncrement():以原子的方式將當前值加1,並返回加1以前的值atom

    4.void lazySet(int newValue):最終會設置成newValue,使用lazySet設置值後,可能致使其餘線程在以後的一小段時間內仍是能夠讀到舊的值。spa

    5.int getAndSet(int newValue):以原子的方式將當前值設置爲newValue,並返回設置以前的舊值線程

  getAndIncremen是如何實現原子操做的呢?讓咱們看一下getAndIncremen的實現代碼,並分析實現原理。getAndIncremen的實現代碼以下:  

public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

  咱們能夠看到getAndIncrement調用了unsafe的getAndAddInt,getAndAddInt的實現:

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

  getAndAddInt調用了Unsafe的native方法:getIntVolatile和compareAndSwapInt,在do-while循環中先取得當前值,而後經過CAS判斷當前值是否和current一致,若是一致意味着值沒被其餘線程修改過,把當前值設置爲當前值+var4,,若是不相等程序進入信的CAS循環。

因爲atomic只提供了int,long和boolean的原子操做類,那麼其餘的基本類型,如byte,char,float,double如何實現原子操做呢,原子操做都是經過Unsafe實現的,讓咱們看一下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);

  Unsafe只提供了三種CAS方法: compareAndSwapObjectcompareAndSwapIntcompateAndSwapLong,其餘類型都是轉成這三種類型再使用對應的方法去原子更新的。

AtomicBoolean介紹與使用

AtomicInteger類的使用

 

2、原子更新數組

  atomic裏提供了三個類用於原子更新數組裏面的元素,分別是:

  AtomicIntegerArray:原子更新整形數組裏的元素

  AtomicLongArray:原子更新長整形數組裏的元素

  AtomicReferenceArray:原子更新引用數組裏的元素

  由於每一個類裏面提供的方法都一致,所以以AtomicIntegerArray爲例來講明。AtomicIntegerArray主要提供了以原子方式更新數組裏的整數,常見方法以下:

  int addAndGet(int i, int delta):以原子的方式將輸入值與數組中索引爲i的元素相加

  boolean compareAndSet(int i, int expect, int update):若是當前值等於預期值,則以原子方式將數組位置i的元素設置成update值。

  實例以下:

public class AtomicIntegerArrayTest {

    private static int[] value = new int[]{1,2,3};
    private static AtomicIntegerArray atomicInteger = new AtomicIntegerArray(value);

    public static void main(String[] args){
        atomicInteger.getAndSet(0,12);
        System.out.println(atomicInteger.get(0));
        System.out.println(value[0]);
    }

}

  須要注意的是,數組value經過構造方法傳遞進去,而後AtomicIntegerArray會將當前數組複製一份,因此當AtomicIntegerArray對內部的數組元素進行修改時,不會影響傳入的數組。

3、原子更新引用類型

  原子更新基本類型的AtomicInteger只能更新一個變量,若是要原子更新多個變量,就須要使用原子更新引用類型提供的類了。原子引用類型atomic包主要提供瞭如下幾個類:

  AtomicReference:原子更新引用類型

  AtomicReferenceFieldUpdater:原子更新引用類型裏的字段

  AtomicMarkableReference:原子更新帶有標記位的引用類型。能夠原子更新一個布爾類型的標記位和引用類型。構造方法是AtomicMarkableReference(V initialRef,booleaninitialMark)

  以上類中提供的方法基本一致,咱們以AtomicReference爲例說明:

public class AtomicReferenceTest {

    private static AtomicReference<User> reference = new AtomicReference<User>();

    public static void main(String[] args){
        User user = new User("tom",23);
        reference.set(user);
        User updateUser = new User("ketty",34);
        reference.compareAndSet(user,updateUser);
        System.out.println(reference.get().getName());
        System.out.println(reference.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 int getAge() {
            return age;
        }
    }
}

上述代碼中首先建立一個user對象,而後把user對象設置進AtomicReference中,最後經過compareAndSet作原子更新操做,運行結果以下:

ketty
34

4、原子更新字段類

若是須要原子更新某個對象的某個字段,就須要使用原子更新屬性的相關類,atomic中提供了一下幾個類用於原子更新屬性:

  AtomicIntegerFieldUpdater:原子更新整形屬性的更新器

  AtomicLongFieldUpdater:原子更新長整形的更新器

  AtomicStampedReference:原子更新帶有版本號的引用類型。該類將整數值與引用關聯起來,可用於原子的更新數據和數據的版本號,能夠解決使用CAS進行原子更新時可能出現的ABA問題。

  想要原子的更新字段,須要兩個步驟:

  1.由於原子更新字段類都是抽象類,每次使用的時候必須使用靜態方法newUpdater()建立一個更新器,而且須要設置想要更新的類和屬性

  2.更新類的字段(屬性)必須使用public volatile修飾符

  本節僅以AstomicIntegerFieldUpdater爲例進行講解,AstomicIntegerFieldUpdater的示例代碼以下:

public class AtomicIntegerFieldUpdaterTest {

    //建立原子更新器
    private static AtomicIntegerFieldUpdater<User> updater = AtomicIntegerFieldUpdater.newUpdater(User.class,"age");

    public static void main(String[] args){
        User user = new User("ketty",21);
        //ketty長了一歲
        updater.getAndIncrement(user);
        System.out.println(updater.get(user));
    }

    static class User{

        private String name;
        public volatile int age;

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

        public String getName() {
            return name;
        }

        public int getAge() {
            return age;
        }
    }
}

  若是age的修飾符號不是 public volatile,回報一下錯誤:

Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: java.lang.IllegalAccessException: Class com.ysl.atomic.AtomicIntegerFieldUpdaterTest can not access a member of class com.ysl.atomic.AtomicIntegerFieldUpdaterTest$User with modifiers "private"
    at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl.<init>(AtomicIntegerFieldUpdater.java:405)
    at java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater(AtomicIntegerFieldUpdater.java:88)
    at com.ysl.atomic.AtomicIntegerFieldUpdaterTest.<clinit>(AtomicIntegerFieldUpdaterTest.java:8)
Caused by: java.lang.IllegalAccessException: Class com.ysl.atomic.AtomicIntegerFieldUpdaterTest can not access a member of class com.ysl.atomic.AtomicIntegerFieldUpdaterTest$User with modifiers "private"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
    at sun.reflect.misc.ReflectUtil.ensureMemberAccess(ReflectUtil.java:103)
    at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl.<init>(AtomicIntegerFieldUpdater.java:394)
    ... 2 more
相關文章
相關標籤/搜索