從原子類和Unsafe來理解Java內存模型,AtomicInteger的incrementAndGet方法和Unsafe部分源碼介紹,valueOffset偏移量的理解

例子

i++的簡單流程

衆所周知,i++分爲三步:html

1. 讀取i的值java

2. 計算i+1安全

3. 將計算出i+1賦給i單元測試

 

保證i++操做的線程安全

用鎖和volatile

能夠使用來保持操做的原子性變量可見性,用volatile保持值的可見性和操做順序性測試

從一個小例子引起的JAVA內存可見性的簡單思考和猜測以及DCL單例模式中的VOLATILE的做用:https://www.cnblogs.com/theRhyme/p/12145461.htmlthis

 

用java.util.concurrent.atomic包下的原子類

若是僅僅是計算操做,咱們天然就想到了java.util.concurrent.atomic包下的原子類,則沒必要考慮鎖的升級、獲取、釋放等消耗,也沒必要考慮鎖的粒度、種類、可重入性等;atom

因爲atomic因爲底層是Unsafe對象的CAS操做,缺點也很明顯:須要循環時間開銷,只能是單個變量CASABA問題(經過AtomicStampedReference解決——增長了stamp相似於version標識)。url

 

AtomicInteger方法源碼

incrementAndGet方法源碼

public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; }

incrementAndGet,先increment,再get,因此獲取的是increment後的值,而unsafe.getAndAddInt先get,因此這裏須要"+1";spa

valueOffset是什麼呢?

  private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } }

AtomicInteger的靜態屬性valueOffset屬性value的偏移量,在類加載的時候,經過AtomicInteger的Field——"value"初始化,後續經過當前的AtomicInteger實例和該valueOffset obtain該實例value屬性的值;.net

 

我的對valueOffset的理解

若是想獲取一個對象的屬性的值,咱們通常經過getter方法得到,而sun.misc.Unsafe卻不一樣,咱們能夠把一個對象實例想象成一塊內存,而這塊內存中包含了一些屬性,如何獲取這塊內存中的某個屬性呢?那就須要該屬性在該內存的偏移量了,每一個屬性在該對象內存中valueOffset偏移量不一樣,每一個屬性的偏移量在該類加載或者以前就已經肯定了(則該類的不一樣實例的同一屬性偏移量相等),因此sun.misc.Unsafe能夠經過一個對象實例該屬性的偏移量用原語得到該對象對應屬性的值

 

sun.misc.Unsafe#getAndAddInt IDEA反編譯後的源碼(與JMM相呼應)

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; }

unsafe.getAndAddInt具體實現是循環不停的compare主存中取到的值var5(this.getIntVolatile)和當前的線程的工做內存中的值(經過對象實例var1和偏移量var2得到),直到二者equal(保證原子性)則更新爲新值var5+var4(delta) ,從這裏咱們也體會到了Java內存模型(線程不能直接操做主存中的值,須要複製一份到本身的工做內存中)

getIntVolatile(主存)和compareAndSwapInt都是sun.misc.Unsafe的native方法。

 

總結

AtomicInteger經過Unsafe對象保證原子性,而Unsafe對象的getAndAddInt方法經過循環比較主存和線程工做內存中的屬性值相等後更新(即CAS)來保證原子性

AtomicInteger的value屬性也被volatile關鍵字修飾:volatile關鍵字的做用

private volatile int value;

 

保證i++原子性的幾種方式

下面是一個很是很是簡單的小例子,分別是非線程安全的i++,鎖同步以及Atomic Class:

public class Counter { @Getter private volatile int i = 0; @Getter private volatile AtomicInteger atomicInteger = new AtomicInteger(0); public void increment(){ i++; // 1. 讀取i的值 // 2. 計算i+1 // 3. 把i+1的值賦給i
 } public void incrementSync(){ synchronized(this) { i++; // 1. 讀取i的值 // 2. 計算i+1 // 3. 把i+1的值賦給i
 } } public void incrementAtomic(){ // 先increment再返回
 atomicInteger.incrementAndGet(); } }

測試類:

public class AtomicityTest { private Counter counter; /** * 每一個線程打印的次數 */
    private int count; @Before public void init(){ counter = new Counter(); count = 10000; } /** * 非線程安全的i++ */ @Test public void increment() throws InterruptedException { Thread t1 = new Thread(() -> { for (int i = 0; i < count; i++) { counter.increment(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < count; i++) { counter.increment(); } }); t1.start(); t2.start(); // 單元測試必須新起的線程要在主線程裏join,不然主線程運行完畢,新起的線程還執行完
 t1.join(); t2.join(); /* ThreeParamsEquals<Enum, Enum, Enum> equals = (Enum p1, Enum p2, Enum p3) -> p1.equals(p2) && p1.equals(p3); while (true){ if (equals.equals(Thread.State.TERMINATED, t1.getState(), t2.getState())) { break; } }*/ System.out.println(t1.getState()); System.out.println(t2.getState()); // 因爲不是原子性操做,兩個線程執行總共20000次i++,結果i的值都小於20000
 System.out.println(counter.getI()); } /** * 線程安全的i++ */ @Test public void incrementSync() throws InterruptedException { Thread t1 = new Thread(() -> { for (int i = 0; i < count; i++) { counter.incrementSync(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < count; i++) { counter.incrementSync(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(t1.getState()); System.out.println(t2.getState()); // 因爲保證了原子性,順序性,可見性操做,兩個線程執行總共20000次i++,結果i的值都等於20000
 System.out.println(counter.getI()); } /** * 原子類AtomicInteger */ @Test public void incrementAtomic() throws InterruptedException { Thread t1 = new Thread(() -> { for (int i = 0; i < count; i++) { counter.incrementAtomic(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < count; i++) { counter.incrementAtomic(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(t1.getState()); System.out.println(t2.getState()); // 因爲保證了原子性,順序性,可見性操做,兩個線程執行總共20000次i++,結果i的值都等於20000
 System.out.println(counter.getAtomicInteger()); } }
相關文章
相關標籤/搜索