當程序更新一個變量時,如果是多線程同時更新這個變量,可能得到的結果與期望值不同。比如:有一個變量i,A線程執行i+1,B線程也執行i+1,經過兩個線程的操作后,變量i的值可能不是期望的3,而是2。這是因為,可能在A線程和B線程執行的時候拿到的i的值都是1,這就是線程不安全的更新操作,通常我們會使用synchronized來解決這個問題,synchronized能保證多線程不會同時更新變量i.
從java1.5開始,jdk提供了java.util.concurrent.atomic包,這個包中的原子操作類,提供了一種用法簡單,性能高效,線程安全的更新一個變量的方式。
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):如果輸入值等於預期值,以原子的方式將該值設置為輸入的值
3.final int getAndIncrement():以原子的方式將當前值加1,並返回加1之前的值
4.void lazySet(int newValue):最終會設置成newValue,使用lazySet設置值后,可能導致其他線程在之后的一小段時間內還是可以讀到舊的值。
5.int getAndSet(int newValue):以原子的方式將當前值設置為newValue,並返回設置之前的舊值
AtomicInteger的使用例子如下:

public class AtomicIntegerTest { private static AtomicInteger atomicInteger = new AtomicInteger(1); public static void main(String[] args){ System.out.println(atomicInteger.getAndIncrement()); System.out.println(atomicInteger.get()); System.out.println(atomicInteger.getAndSet(5)); System.out.println(atomicInteger.get()); } }
輸出結果如下:
1 2 2 5
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只提供了3種CAS方法:compareAndSwapObject、compareAndSwapInt和compareAndSwapLong,再看AtomicBoolean源碼,發現它是先把Boolean轉換成整型,再使用compareAndSwapInt進行CAS,所以原子更新char、float和double變量也可以用類似的思路來實現。
二.原子更新數組
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