??當(dāng)程序更新一個變量時, 如果多線程同時更新這個變量蛙吏, 可能得到期望之外的值源哩, 比如變量 i=l, A 線程更新 i+l, B 線程也更新 1葉, 經(jīng)過兩個線程操作之后可能 i 不等于3鸦做,而是等于2励烦。 因為A和B線程在更新變量i 的時候拿到的i 都是1,這就是線程不安全的更新操作泼诱, 通常我們會使用 synchronized 來解決這個問題坛掠, synchronized 會保證多線程不會同時更 新變量i。
??而 Java 從 JDK1.5開始提供了java.util.concurrent.atomic 包(以下簡稱 Atomic 包)治筒, 這個包中的原子操作類提供了一種用法簡單屉栓、 性能高效、 線程安全地更新一個變量的方式矢炼。
??因為變量的類型有很多種系瓢, 所以在 Atomic 包里一共提供了 13 個類, 屬于4 種類型的原子更新方式句灌, 分別是原子更新基本類型夷陋、 原子更新數(shù)組、 原子更新引用和原子更新屬性(字段) 胰锌。 Atomic 包里的類基本都是使用 Unsafe 實現(xiàn)的包裝類骗绕。
1.原子更新基本類型類
??使用原子的方式更新基本類型, Atomic 包提供了以下 3 個類资昧。
??AtomicBoolean :原子更新布爾類型酬土。
??Atomiclnteger :原子更新整型。
??AtomicLong:原子更新長整型格带。
??以上3個類提供的方法幾乎一模一樣撤缴,我們以AtomicInteger為例說明一下:
??int addAndGet ( int delta ): 以原子方式將輸入的數(shù)值與實例中的值( Atomiclnteger 里
的 value )相加, 并返回結(jié)果叽唱。
??boolean compareAndSet ( int expect, int update ):如果輸入的數(shù)值等于預(yù)期值屈呕, 則以原
子方式將該值設(shè)置為輸入的值。
??int getAndlncrement():以原子方式將當(dāng)前值加 l 棺亭, 注意虎眨, 這里返回的是自增前的值。
??void IazySet ( int newValue ):最終會設(shè)置成 newValue,使用 lazySet 設(shè)置值后嗽桩, 可能導(dǎo)致其他線程在之后的一小段時間內(nèi)還是可以讀到舊的值岳守。
??int getAndSet (int newValue ):以原子方式設(shè)置為newValue 的值, 并返回舊值碌冶。
2. 原子更新數(shù)組
??AtomicLongArray:原子更新長整型數(shù)組里的元素湿痢。
??AtomicReferenceArray:原子更新引用類型數(shù)組里的元素。
??AtomicintegerArray類主要是提供原子的方式更新數(shù)組里的整型种樱,其常用方法如下蒙袍。
????intaddAndGet ( int i, int delta):以原子方式將輸入值與數(shù)組中索引i的元素相加俊卤。
????boolean compareAndSet ( int i, int expect, int update):如果當(dāng)前值等于預(yù)期值嫩挤,則以原子方式將數(shù)組位置i的元素設(shè)置成update值。
3. 原子引用類型
??原子更新基本類型的Atomiclnteger消恍,只能更新一個變量岂昭,如果要原子更新多個變量,就需要使用這個原子更新引用類型提供的類狠怨。Atomic包提供了以下3個類约啊。
??AtomicReference:原子更新引用類型。
??AtomicReferenceFieldU pdater:原子更新引用類型里的字段佣赖。
??AtomicMarkableReference:原子更新帶有標(biāo)記位的引用類型恰矩。可以原子更新一個布爾類型的標(biāo)記位和引用類型憎蛤。構(gòu)造方法是AtomicMarkableReference ( V initia!Ref, boolean initia!Mark)外傅。
4.原子更新字段類
??如果需原子地更新某個類里的某個字段時,就需要使用原子更新字段類俩檬, Atomic 包提供了以下3個類進(jìn)行原子字段更新萎胰。
??AtomiclntegerFieldUpdater :原子更新整型的字段的更新器。
??AtomicLongFieldUpdater :原子更新長整型宇段的更新器棚辽。
??AtomicStampedReference :原子更新帶有版本號的引用類型技竟。該類將整數(shù)值與引用關(guān)聯(lián)起來,可用于原子的更新數(shù)據(jù)和數(shù)據(jù)的版本號屈藐,可以解決使用CAS進(jìn)行原子更新時可能出現(xiàn)的ABA問題榔组。
??要想原子地更新字段類需要兩步。第一步联逻,因為原子更新宇段類都是抽象類搓扯, 每次使用的時候必須使用靜態(tài)方法 newUpdater()創(chuàng)建一個更新器,并且需要設(shè)置想要更新的類和屬性遣妥。第二步擅编,更新類的字段(屬性)必須使用 public volatile 修飾符。