轉自https://www.cnblogs.com/rever/p/8215743.htmlhtml
在進行併發編程的時候咱們須要確保程序在被多個線程併發訪問時能夠獲得正確的結果,也就是實現線程安全。線程安全的定義以下:java
當多個線程訪問某個類時,無論運行時環境採用何種調度方式或者這些線程將如何交替執行,而且在主調代碼中不須要任何額外的同步或協同,這個類都能表現出正確的行爲,那麼這個類就是線程安全的。編程
舉個線程不安全的例子。假如咱們想實現一個功能來統計網頁訪問量,你可能想到用count++
來統計訪問量,可是這個自增操做不是線程安全的。count++
能夠分紅三個操做:緩存
假設count的初始值爲10,當進行併發操做的時候,可能出現線程A和線程B都進行到了1操做,以後又同時進行2操做。A先進行到3操做+1,如今值爲11;注意剛纔AB獲取到的當前值都是10,因此B執行3操做後,count的值依然是11。這個結果顯然不符合咱們的要求。安全
因此咱們須要用本篇的主角—— AtomicInteger 來保證線程安全。併發
AtomicInteger 的源碼以下:app
package java.util.concurrent.atomic; import sun.misc.Unsafe; public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile int value; public AtomicInteger(int initialValue) { value = initialValue; } public AtomicInteger() { } public final int get() { return value; } public final void set(int newValue) { value = newValue; } public final void lazySet(int newValue) { unsafe.putOrderedInt(this, valueOffset, newValue); } public final int getAndSet(int newValue) { for (;;) { int current = get(); if (compareAndSet(current, newValue)) return current; } } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } public final boolean weakCompareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } } public final int getAndDecrement() { for (;;) { int current = get(); int next = current - 1; if (compareAndSet(current, next)) return current; } } public final int getAndAdd(int delta) { for (;;) { int current = get(); int next = current + delta; if (compareAndSet(current, next)) return current; } } public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } } public final int decrementAndGet() { for (;;) { int current = get(); int next = current - 1; if (compareAndSet(current, next)) return next; } } public final int addAndGet(int delta) { for (;;) { int current = get(); int next = current + delta; if (compareAndSet(current, next)) return next; } } public String toString() { return Integer.toString(get()); } public int intValue() { return get(); } public long longValue() { return (long)get(); } public float floatValue() { return (float)get(); } public double doubleValue() { return (double)get(); } }
咱們先看原子整型類中定義的屬性工具
// setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } }
Unsafe是JDK內部的工具類,主要實現了平臺相關的操做。下面內容引自性能
sun.misc.Unsafe是JDK內部用的工具類。它經過暴露一些Java意義上說「不安全」的功能給Java層代碼,來讓JDK可以更多的使用Java代碼來實現一些本來是平臺相關的、須要使用native語言(例如C或C++)才能夠實現的功能。該類不該該在JDK核心類庫以外使用。this
Unsafe的具體實現跟本篇的目標關聯不大,你只要知道這段代碼是爲了獲取value在堆內存中的偏移量就夠了。偏移量在AtomicInteger中很重要,原子操做都靠它來實現。
AtomicInteger 自己是個整型,因此最重要的屬性就是value,咱們看看它是如何聲明value的
private volatile int value;
咱們看到value使用了volatile
修飾符,那麼什麼是volatile
呢?
volatile至關於synchronized
的弱實現,也就是說volatile
實現了相似synchronized
的語義,卻又沒有鎖機制。它確保對volatile
字段的更新以可預見的方式告知其餘的線程。
volatile
包含如下語義:
- Java 存儲模型不會對valatile指令的操做進行重排序:這個保證對volatile變量的操做時按照指令的出現順序執行的。
- volatile變量不會被緩存在寄存器中(只有擁有線程可見)或者其餘對CPU不可見的地方,每次老是從主存中讀取volatile變量的結果。也就是說對於volatile變量的修改,其它線程老是可見的,而且不是使用本身線程棧內部的變量。也就是在happens-before法則中,對一個valatile變量的寫操做後,其後的任何讀操做理解可見此寫操做的結果。
簡而言之volatile 的做用是當一個線程修改了共享變量時,另外一個線程能夠讀取到這個修改後的值。在分析AtomicInteger 源碼時,咱們瞭解到這裏就足夠了。
AtomicInteger中有不少方法,例如incrementAndGet()
至關於i++
和getAndAdd()
至關於i+=n
。從源碼中咱們能夠看出這幾種方法的實現很類似,因此咱們主要分析incrementAndGet()
方法的源碼。
源碼以下:
public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
incrementAndGet()
方法實現了自增的操做。核心實現是先獲取當前值和目標值(也就是value+1),若是compareAndSet(current, next)
返回成功則該方法返回目標值。那麼compareAndSet是作什麼的呢?理解這個方法咱們須要引入CAS操做。
在大學操做系統課程中咱們學過獨佔鎖和樂觀鎖的概念。獨佔鎖就是線程獲取鎖後其餘的線程都須要掛起,直到持有獨佔鎖的線程釋放鎖;樂觀鎖是先假定沒有衝突直接進行操做,若是由於有衝突而失敗就重試,直到操做成功。其中樂觀鎖用到的機制就是CAS,Compare and Swap。
AtomicInteger 中的CAS操做就是compareAndSet()
,其做用是每次從內存中根據內存偏移量(valueOffset
)取出數據,將取出的值跟expect 比較,若是數據一致就把內存中的值改成update。
這樣使用CAS就保證了原子操做。其他幾個方法的原理跟這個相同,在此再也不過多的解釋。
沒看AtomicInteger 源碼以前,我認爲其內部是用synchronized
來實現的原子操做。查閱資料後發現synchronized
會影響性能,由於Java中的synchronized
鎖是獨佔鎖,雖然能夠實現原子操做,可是這種實現方式的併發性能不好。
總結一下,AtomicInteger 中主要實現了整型的原子操做,防止併發狀況下出現異常結果,其內部主要依靠JDK 中的unsafe 類操做內存中的數據來實現的。volatile 修飾符保證了value在內存中其餘線程能夠看到其值得改變。CAS操做保證了AtomicInteger 能夠安全的修改value 的值。