深入解析Java AtomicInteger 原子类型
深入解析Java AtomicInteger原子類型
在進行并發編程的時候我們需要確保程序在被多個線程并發訪問時可以得到正確的結果,也就是實現線程安全。線程安全的定義如下:
當多個線程訪問某個類時,不管運行時環境采用何種調度方式或者這些線程將如何交替執行,并且在主調代碼中不需要任何額外的同步或協同,這個類都能表現出正確的行為,那么這個類就是線程安全的。
舉個線程不安全的例子。假如我們想實現一個功能來統計網頁訪問量,你可能想到用count++?來統計訪問量,但是這個自增操作不是線程安全的。count++?可以分成三個操作:
假設count的初始值為10,當進行并發操作的時候,可能出現線程A和線程B都進行到了1操作,之后又同時進行2操作。A先進行到3操作+1,現在值為11;注意剛才AB獲取到的當前值都是10,所以B執行3操作后,count的值依然是11。這個結果顯然不符合我們的要求。
所以我們需要用本篇的主角—— AtomicInteger 來保證線程安全。
AtomicInteger 的源碼如下:
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 updatesprivate 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 updatesprivate 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核心類庫之外使用。
Unsafe的具體實現跟本篇的目標關聯不大,你只要知道這段代碼是為了獲取value在堆內存中的偏移量就夠了。偏移量在AtomicInteger中很重要,原子操作都靠它來實現。
Value的定義和volatile
AtomicInteger 本身是個整型,所以最重要的屬性就是value,我們看看它是如何聲明value的
private volatile int value;我們看到value使用了volatile修飾符,那么什么是volatile呢?
volatile相當于synchronized的弱實現,也就是說volatile實現了類似synchronized的語義,卻又沒有鎖機制。它確保對volatile字段的更新以可預見的方式告知其他的線程。
volatile包含以下語義:
簡而言之volatile 的作用是當一個線程修改了共享變量時,另一個線程可以讀取到這個修改后的值。在分析AtomicInteger 源碼時,我們了解到這里就足夠了。
用CAS操作實現安全的自增
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 的值。
from:?https://www.cnblogs.com/rever/p/8215743.html?
總結
以上是生活随笔為你收集整理的深入解析Java AtomicInteger 原子类型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 并发锁之二:ReentrantReadW
- 下一篇: 原子操作类AtomicInteger详解