當程序更新一個變量時,若是是多線程同時更新這個變量,可能獲得的結果與指望值不一樣。好比:有一個變量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實現的包裝類。數組
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方法: compareAndSwapObject、compareAndSwapInt和compateAndSwapLong,其餘類型都是轉成這三種類型再使用對應的方法去原子更新的。
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對內部的數組元素進行修改時,不會影響傳入的數組。
原子更新基本類型的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
若是須要原子更新某個對象的某個字段,就須要使用原子更新屬性的相關類,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