當程序更新一個變量時,如果多線程同時更新這個變量,可能得到期望之外的值,通常會使用synchronized.
Java從JDK 1.5開始提供了java.util.concurrent.atomic包(以下簡稱Atomic包),這個包中的原子操作類提供了一種用法簡單、性能高效、線程安全地更新一個變量的方式。
實際上JDK1.8中在JUC中只有12個類:

原子更新基本類型
使用原子方式更新基本類型,Atomic包提供了以下3個類:
- AtomicBoolean:原子更新布爾類型。
- AtomicInteger:原子更新整型。
- AtomicLong:原子更新長整型。
上述的三個類的方法基本上是一致的,所以以AtomicInteger為例進行講解;
AtomicInteger常用的方法:
- ·int addAndGet(int delta):以原子方式將輸入的數(shù)值與實例中的值(AtomicInteger里的value)相加,并返回結(jié)果。
- boolean compareAndSet(int expect,int update):如果輸入的數(shù)值等于預(yù)期值,則以原子方式將該值設(shè)置為輸入的值。
- int getAndIncrement():以原子方式將當前值加1,注意,這里返回的是自增前的值。
-
void lazySet(int newValue):最終會設(shè)置成newValue,使用lazySet設(shè)置值后,可能導(dǎo)致其他線程在之后的一小段時間內(nèi)還是可以讀到舊的值。
getAndIncrement()
源碼中for循環(huán)體的第一步先取得AtomicInteger里存儲的數(shù)值,第二步對AtomicInteger的當前數(shù)值進行加1操作,關(guān)鍵的第三步調(diào)用compareAndSet方法來進行原子更新操作,該方法先檢查當前數(shù)值是否等于current,等于意味著AtomicInteger的值沒有被其他線程修改過,則將AtomicInteger的當前數(shù)值更新成next的值,如果不等compareAndSet方法會返回false,程序會進入for循環(huán)重新進行compareAndSet操作。
Atomic包里的類基本都是使用Unsafe實現(xiàn)的

AtomicBoolean源碼中,發(fā)現(xiàn)它是先把Boolean轉(zhuǎn)換成整型,再使用compareAndSwapInt進行CAS,所以原子更新char、float和double變量也可以用類似的思路來實現(xiàn)。
原子更新數(shù)組
通過原子方式更新數(shù)組里某個元素,Atomic包提供可以下3個類:
- AtomicIntegerArray:原子更新整型數(shù)組里的元素。
- AtomicLongArray:原子更新長整型數(shù)組里的元素。
- AtomicReferenceArray:原子更新引用類型數(shù)組里的元素。
AtomicIntegerArray類主要是提供原子的方式更新數(shù)組里的整型,其常用方法如下。
- int addAndGet(int i,int delta):以原子方式將輸入值與數(shù)組中索引i的元素相加。
- boolean compareAndSet(int i,int expect,int update):如果當前值等于預(yù)期值,則以原子方式將數(shù)組位置i的元素設(shè)置成update值。
數(shù)組通過構(gòu)造方法傳遞進去,然后AtomicIntegerArray會將當前數(shù)組復(fù)制一份,所以當AtomicIntegerArray對內(nèi)部的數(shù)組元素進行修改時,不會影響傳入的數(shù)組
原子更新引用類型
原子更新基本類型的AtomicInteger,只能更新一個變量,如果要原子更新多個變量,就需要使用這個原子更新引用類型提供的類。
- AtomicReference:原子更新引用類型。
- AtomicReferenceFieldUpdater:原子更新引用類型里的字段。
- AtomicMarkableReference:原子更新帶有標記位的引用類型。
可以用原子更新一個布爾類型的標記位和引用類型。構(gòu)造方法是AtomicMarkableReference(V initialRef,boolean initialMark)。
僅以AtomicReference為例

代碼中首先會創(chuàng)建一個user對象,把user對象設(shè)置進行AtomicReference中,最后調(diào)用compareAndSet方法進行原子更新操作,實現(xiàn)原理同AtomicInteger里的compareAndSet方法。代碼執(zhí)行后輸出結(jié)果如下。

原子更新字段類
- AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
- AtomicLongFieldUpdater:原子更新長整型字段的更新器。
- AtomicStampedReference:原子更新帶有版本號的引用類型。
該類型將整數(shù)值與引用關(guān)聯(lián)起來,可用于原子的更新數(shù)據(jù)和數(shù)據(jù)的版本號.可以解決使用CAS進行原子更新時可能出現(xiàn)的ABA問題。(利用版本號解決ABA問題).
要想原子地更新字段類需要兩步.
- 第一步,因為原子更新字段都是抽象類,每次使用的時候必須使用靜態(tài)方法new Updater()創(chuàng)建一個更新器,并且設(shè)置想要更新的類和屬性.
- 更新類的字段(屬性)必須使用public volatile修飾符.
以AtomicIntegerFieldUpdater為例:
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public class AtomicIntegerFieldUpdaterTest {
private static AtomicIntegerFieldUpdater<User> updater = AtomicIntegerFieldUpdater.newUpdater(User.class,"old");
public static void main(String[] args) {
//設(shè)置柯南的年齡為10歲
User conan = new User("conan",10);
//柯南長了一歲,但是仍然會輸出舊的年齡
System.out.println(updater.getAndIncrement(conan));
//輸出柯南現(xiàn)在的年齡
System.out.println(updater.get(conan));
}
public static class User {
private String name;
public volatile int old;
public User(String name,int old) {
this.name = name;
this.old = old;
}
public String getName() {
return name;
}
public int getOld() {
return old;
}
}
}
參考書籍:《Java并發(fā)編程的藝術(shù)》
