Atomic 翻譯成中文是原子的意思。在化學(xué)上,我們知道原子是構(gòu)成一般物質(zhì)的最小單位,在化學(xué)反應(yīng)中是不可分割的。在我們這里 Atomic 是指一個操作是不可中斷的。即使是在多個線程一起執(zhí)行的時候,一個操作一旦開始,就不會被其他線程干擾。所以,所謂原子類說簡單點就是具有原子/原子操作特征的類。并發(fā)包 java.util.concurrent 的原子類都存放在 java.util.concurrent.atomic 下,如下圖所示。
根據(jù)操作的數(shù)據(jù)類型,可以將JUC包中的原子類分為4類
基本類型
使用原子的方式更新基本類型
AtomicInteger:整型原子類
AtomicLong:長整型原子類
AtomicBoolean :布爾型原子類
數(shù)組類型
使用原子的方式更新數(shù)組里的某個元素
AtomicIntegerArray:整型數(shù)組原子類
AtomicLongArray:長整型數(shù)組原子類
AtomicReferenceArray :引用類型是數(shù)組原子類
引用類型
AtomicReference:引用類型原子類
AtomicMarkableReference:原子更新帶有標記的引用類型。該類將 boolean 標記與引用關(guān)聯(lián)起來,也可以解決使用 CAS 在進行原子更新時可能出現(xiàn)的 ABA 問題。
AtomicStampedReference :原子更新帶有版本號的引用類型。該類將整數(shù)值與引用關(guān)聯(lián)起來,可用于解決原子的更新數(shù)據(jù)和數(shù)據(jù)的版本號,可以解決使用 CAS 進行原子更新時可能出現(xiàn)的 ABA問題。
對象的屬性修改類型
AtomicIntegerFieldUpdater:原子更新整型字段的更新器
AtomicLongFieldUpdater:原子更新長整型字段的更新器
AtomicReferenceFieldUpdater:原子更新引用類型里的字段
修正: AtomicMarkableReference 不能解決ABA問題
/**AtomicMarkableReference是將一個boolean值作是否有更改的標記,本質(zhì)就是它的版本號只有兩個,true和false,修改的時候在這兩個版本號之間來回切換,這樣做并不能解決ABA的問題,只是會降低ABA問題發(fā)生的幾率而已
*/
public class SolveABAByAtomicMarkableReference {
private static AtomicMarkableReference atomicMarkableReference = new
AtomicMarkableReference(100, false);
public static void main(String[] args) {
Thread refT1 = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicMarkableReference.compareAndSet(100, 101,
atomicMarkableReference.isMarked(), !atomicMarkableReference.isMarked());
atomicMarkableReference.compareAndSet(101, 100,
atomicMarkableReference.isMarked(), !atomicMarkableReference.isMarked());
});
Thread refT2 = new Thread(() -> {
boolean marked = atomicMarkableReference.isMarked();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean c3 = atomicMarkableReference.compareAndSet(100, 101,
marked, !marked);
System.out.println(c3); // 返回true,實際應(yīng)該返回false
});
refT1.start();
refT2.start();
}
}
CAS ABA 問題
描述: 第一個線程取到了變量 x 的值 A,然后巴拉巴拉干別的事,總之就是只拿到了變量 x 的值A(chǔ)。這段時間內(nèi)第二個線程也取到了變量 x 的值 A,然后把變量 x 的值改為 B,然后巴拉巴拉干別的事,最后又把變量 x 的值變?yōu)?A (相當(dāng)于還原了)。在這之后第一個線程終于進行了變量 x 的操作,但是此時變量 x 的值還是 A,所以 compareAndSet 操作是成功。例子描述(可能不太合適,但好理解): 年初,現(xiàn)金為零,然后通過正常勞動賺了三百萬,之后正常消費了(比如買房子)三百萬。年末,雖然現(xiàn)金零收入(可能變成其他形式了),但是賺了錢是事實,還是得交稅的!
代碼例子(以 AtomicInteger 為例)
【關(guān)注】轉(zhuǎn)發(fā)了解更多內(nèi)容,方便后續(xù)查看