Atomic的介绍和使用(原子变量)
開始之前,我們來看一下上一篇文章中《CAS (全 ) && concurrent包的實(shí)現(xiàn)》中提到了concurrent包的實(shí)現(xiàn)圖。
 下圖中的原子變量類就是Atomic類中的一部分。
 也就是說,atomic類首先是一個(gè)樂觀鎖,然后底層實(shí)現(xiàn)也是根據(jù)CAS操作和Volatile關(guān)鍵字實(shí)現(xiàn)的。
 
什么是線程安全?
 **線程安全是指多線程訪問是時(shí),無論線程的調(diào)度策略是什么,程序能夠正確的執(zhí)行。**導(dǎo)致線程不安全的一個(gè)原因是狀態(tài)不一致,如果線程A修改了某個(gè)共享變量(比如給id++),而線程B沒有及時(shí)知道,就會導(dǎo)致B在錯(cuò)誤的狀態(tài)上執(zhí)行,結(jié)果的正確性也就無法保證。原子變量為我們提供了一種保證單個(gè)狀態(tài)一致的簡單方式,一個(gè)線程修改了原子變量,另外的線程立即就能看到,這比通過鎖實(shí)現(xiàn)的方式效率要高;如果要同時(shí)保證多個(gè)變量狀態(tài)一致,就只能使用鎖了。
Atomic
在JDK1.5之后,JDK的(concurrent包)并發(fā)包里提供了一些類來支持原子操作,如AtomicBoolean,AtomicInteger,AtomicLong都是用原子的方式來更新指定類型的值。
從多線程并行計(jì)算樂觀鎖 和 悲觀鎖 來講,JAVA中的synchronized 屬于悲觀鎖,即是在操作某數(shù)據(jù)的時(shí)候總是會認(rèn)為多線程之間會相互干擾,屬于阻塞式的加鎖;Atomic系列則屬于樂觀鎖系列,即當(dāng)操作某一段數(shù)據(jù)的時(shí)候,線程之間是不會相互影響,采用非阻塞的模式,直到更新數(shù)據(jù)的時(shí)候才會進(jìn)行版本的判斷是否值已經(jīng)進(jìn)行了修改。
sun.misc.Unsafe
這個(gè)類包含了大量的對C代碼的操作,包括了很多直接內(nèi)存分配和原子操作的調(diào)用,都存在安全隱患,所以標(biāo)記為unsafe。
 AtomicInteger是一個(gè)標(biāo)準(zhǔn)的樂觀鎖實(shí)現(xiàn),sun.misc.Unsafe是JDK提供的樂觀鎖的支持。
Atomic在JAVA中的家族如下:
 a、基本類:AtomicInteger、AtomicLong、AtomicBoolean;
 b、引用類型:AtomicReference、AtomicReference的ABA實(shí)例、AtomicStampedRerence、AtomicMarkableReference;
 c、數(shù)組類型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
 d、屬性原子修改器(Updater):AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
Atomic的使用測試和講解 見:https://blog.csdn.net/xh16319/article/details/17056767
 這里我們不對每一個(gè)Atomic類進(jìn)行詳細(xì)的測試和講解,我們只需要知道Atomic底層是通過CAS操作實(shí)現(xiàn)的數(shù)據(jù)的原子性,其中AtomicStampedReference類解決了ABA問題(ABA問題在《CAS (全 ) && concurrent包的實(shí)現(xiàn)》中也有講解)
AtomicInteger
AtomicInteger 類主要利用 CAS (compare and swap) + volatile 和 native 方法來保證原子操作,從而避免 synchronized 的高開銷,執(zhí)行效率大為提升。
CAS的原理是拿期望的值和原本的一個(gè)值作比較,如果相同則更新成新的值。UnSafe 類的 objectFieldOffset() 方法是一個(gè)本地方法,這個(gè)方法是用來拿到“原來的值”的內(nèi)存地址。另外 value 是一個(gè)volatile變量,在內(nèi)存中可見,因此 JVM 可以保證任何時(shí)刻任何線程總能拿到該變量的最新值。
 部分源碼:
常用方法
AtomicInteger和AtomicLong、AtomicBoolean三個(gè)類提供的方法幾乎相同。
 AtomicInteger 類常用方法:
示例:
import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerTest {public static void main(String[] args) {// TODO Auto-generated method stubint temvalue = 0;AtomicInteger i = new AtomicInteger(0);temvalue = i.getAndSet(3);System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:0; i:3temvalue = i.getAndIncrement();System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:3; i:4temvalue = i.getAndAdd(5);System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:4; i:9}}使用Synchronized和AtomicInteger的示例:
不使用AtomicInteger而是使用基本類型:
class Test {private volatile int count = 0;//若要線程安全執(zhí)行執(zhí)行count++,需要加鎖public synchronized void increment() {count++; }public int getCount() {return count;} }使用AtomicInteger:
class Test2 {private AtomicInteger count = new AtomicInteger();public void increment() {count.incrementAndGet();}//使用AtomicInteger之后,不需要加鎖,也可以實(shí)現(xiàn)線程安全。public int getCount() {return count.get();} }AtomicStampedReference 類和AtomicMarkableReference類
我們在這里只對 AtomicStampedReference 類和AtomicMarkableReference類的使用進(jìn)行簡單描述,讀者理解其用法即可。
import java.util.concurrent.atomic.AtomicStampedReference; public class AtomicStampedReferenceTest { public final static AtomicStampedReference <String>ATOMIC_REFERENCE = new AtomicStampedReference<String>("abc" , 0); public static void main(String []args) { for(int i = 0 ; i < 100 ; i++) { final int num = i; final int stamp = ATOMIC_REFERENCE.getStamp(); new Thread() { public void run() { try { Thread.sleep(Math.abs((int)(Math.random() * 100))); } catch (InterruptedException e) { e.printStackTrace(); } if(ATOMIC_REFERENCE.compareAndSet("abc" , "abc2" , stamp , stamp + 1)) { System.out.println("我是線程:" + num + ",我獲得了鎖進(jìn)行了對象修改!"); } } }.start(); } //下面這個(gè)線程是為了將數(shù)據(jù)改回原始值,以便之后的操作new Thread() { public void run() { int stamp = ATOMIC_REFERENCE.getStamp(); while(!ATOMIC_REFERENCE.compareAndSet("abc2", "abc" , stamp , stamp + 1)); System.out.println("已經(jīng)改回為原始值!"); } }.start(); } }其中的compareAndSet函數(shù)的源碼中就用到了之前文章中講解的CAS操作:
public final boolean compareAndSet(V expect, V update) { return unsafe.compareAndSwapObject(this, valueOffset, expect, update); }類:AtomicMarkableReference和AtomicStampedReference功能差不多,有點(diǎn)區(qū)別的是:AtomicMarkableReference描述更加簡單的是與否的關(guān)系,通常ABA問題只有兩種狀態(tài),而AtomicStampedReference是多種狀態(tài),那么為什么還要有AtomicMarkableReference呢,因?yàn)樗谔幚硎桥c否上面更加具有可讀性,而AtomicStampedReference過于隨意定義狀態(tài),并不便于閱讀大量的是和否的關(guān)系,它可以被認(rèn)為是一個(gè)計(jì)數(shù)器或狀態(tài)列表等信息,java提倡通過類名知道其意義,所以這個(gè)類的存在也是必要的,它的定義就是將數(shù)據(jù)變換為true|false如下:
public final static AtomicMarkableReference <String>ATOMIC_MARKABLE_REFERENCE = new AtomicMarkableReference<String>("abc" , false);操作時(shí)使用:
ATOMIC_MARKABLE_REFERENCE.compareAndSet("abc", "abc2", false, true);另外我們來簡單介紹一些Atomic中的擴(kuò)展類:
 java提供一個(gè)外部的Updater可以對對象的屬性本身的修改提供類似Atomic的操作,也就是它對這些普通的屬性的操作是并發(fā)下安全的,分別由:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceUpdater。
這樣操作后,系統(tǒng)會更加靈活,也就是可能那些類的屬性只是在某些情況下需要控制并發(fā),很多時(shí)候不需要,但是他們的使用通常有以下幾個(gè)限制:
 限制1:操作的目標(biāo)不能是static類型,前面說到unsafe的已經(jīng)可以猜測到它提取的是非static類型的屬性偏移量,如果是static類型在獲取時(shí)如果沒有使用對應(yīng)的方法是會報(bào)錯(cuò)的,而這個(gè)Updater并沒有使用對應(yīng)的方法。
限制2:操作的目標(biāo)不能是final類型的,因?yàn)閒inal根本沒法修改。
**限制3:必須是volatile類型的數(shù)據(jù),**也就是數(shù)據(jù)本身是讀一致的。
限制4:屬性必須對當(dāng)前的Updater所在的區(qū)域是可見的,也就是private如果不是當(dāng)前類肯定是不可見的,protected如果不存在父子關(guān)系也是不可見的,default如果不是在同一個(gè)package下也是不可見的。
實(shí)現(xiàn)方式:通過反射找到屬性,對屬性進(jìn)行操作,但是并不是設(shè)置accessable,所以必須是可見的屬性才能操作。
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; public class AtomicIntegerFieldUpdaterTest { static class A { volatile int intValue = 100; } /** * 可以直接訪問對應(yīng)的變量,進(jìn)行修改和處理 * 條件:要在可訪問的區(qū)域內(nèi),如果是private或挎包訪問default類型以及非父親類的protected均無法訪問到 * 其次訪問對象不能是static類型的變量(因?yàn)樵谟?jì)算屬性的偏移量的時(shí)候無法計(jì)算),也不能是final類型的變量(因?yàn)楦緹o法修改),必須是普通的成員變量 * * 方法(說明上和AtomicInteger幾乎一致,唯一的區(qū)別是第一個(gè)參數(shù)需要傳入對象的引用) * @see AtomicIntegerFieldUpdater#addAndGet(Object, int) * @see AtomicIntegerFieldUpdater#compareAndSet(Object, int, int) * @see AtomicIntegerFieldUpdater#decrementAndGet(Object) * @see AtomicIntegerFieldUpdater#incrementAndGet(Object) * * @see AtomicIntegerFieldUpdater#getAndAdd(Object, int) * @see AtomicIntegerFieldUpdater#getAndDecrement(Object) * @see AtomicIntegerFieldUpdater#getAndIncrement(Object) * @see AtomicIntegerFieldUpdater#getAndSet(Object, int) */ public final static AtomicIntegerFieldUpdater <A>ATOMIC_INTEGER_UPDATER = AtomicIntegerFieldUpdater.newUpdater(A.class, "intValue"); public static void main(String []args) { final A a = new A(); for(int i = 0 ; i < 100 ; i++) { final int num = i; new Thread() { public void run() { if(ATOMIC_INTEGER_UPDATER.compareAndSet(a, 100, 120)) { System.out.println("我是線程:" + num + " 我對對應(yīng)的值做了修改!"); } } }.start(); } } }總結(jié)
以上是生活随笔為你收集整理的Atomic的介绍和使用(原子变量)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: Mac终端链接服务器记住密码
 - 下一篇: 【逗老师的无线电】宝峰神机刷OpenGD