咱們都知道在java中long類型變量佔用的字節數是8也就是64位,而在32位的操做系統對64位的數據讀寫要分紅兩步完成,每一步取32位操做。這樣的話JVM就不能保證對long和double賦值操做的原子性,由於多線程環境下有可能出現這樣一種狀況,兩個線程同時寫一個多線程共享變量(主內存)一個寫低32位而另外一個線程高32位或者一個線程在兩步寫中間過程當中穿插了另外一個線程的兩步寫操做,這樣就有可能致使用戶獲取的64位long變量是是無效的髒數據。咱們有兩種方法來解決此類現象:第1種,使用volatile修飾long變量有的同窗很奇怪volatile不是隻保證變量多線程修改的可見性和字節碼指令重排的局部有序性嗎,注意java內存模型爲咱們保證了聲明爲volatile的long類型變量的賦值和獲取操做是原子操做;第2種方式就是本文要講的直接使用JUC(java.util.current包)提供的原子類AtomicLong保證長整形操做的原子性。java
package java.util.concurrent.atomic; import java.util.function.LongUnaryOperator; import java.util.function.LongBinaryOperator; import sun.misc.Unsafe; public class AtomicLong extends Number implements java.io.Serializable
private static final long serialVersionUID = 1927816293512124184L; //Unsafe類提供了硬件級別的原子操做,經過它的compareAndSwapLong方法對long變量進行原子更新 private static final Unsafe unsafe = Unsafe.getUnsafe(); //我的理解valueOffset保存的信息相似AtomicLong內部long值的當前版本號,由於AtomicLong支持延後更新當前值不見得 //就是最新值,Unsafe進行cas操做時經過valueOffset去獲取最近一次更新後的long值 private static final long valueOffset; //標識當前JVM所在的操做系統是否支持long變量操做的原子性,其實個人理解就是判斷當前操做系統是否支持對8字節也就是64 //位數據的操做原子性,再簡單點就是判斷該操做系統是32位的仍是64位的 static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8(); private static native boolean VMSupportsCS8(); static { try { valueOffset = unsafe.objectFieldOffset (AtomicLong.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } //存儲的long類型數據使用volatile修飾保證它修改的多線程可見性 private volatile long value;
public AtomicLong(long initialValue) { value = initialValue; } public AtomicLong() { }
//獲取當前值 public final long get() { return value; } //設置當前值爲newValue,該操做是原子操做,爲啥參考前面 public final void set(long newValue) { value = newValue; } //延時設置當前變量爲新值newValue,這個方法區別於set方法的點在於它在後臺修改值且不等待修改完成當即返回,若是程序 //不須要當即獲取更新後的值那麼使用這個方法性能會好點 public final void lazySet(long newValue) { unsafe.putOrderedLong(this, valueOffset, newValue); } //以原子操做方式更新當前值爲newValue返回舊值 public final long getAndSet(long newValue) { return unsafe.getAndSetLong(this, valueOffset, newValue); } //若是當前值等於預期值expect,則以原子方式基於無鎖cas將當前值更新爲update public final boolean compareAndSet(long expect, long update) { return unsafe.compareAndSwapLong(this, valueOffset, expect, update); } //坑爹掛羊頭賣狗肉,實現和接口定義不一致,做用和compareAndSet等同 public final boolean weakCompareAndSet(long expect, long update) { return unsafe.compareAndSwapLong(this, valueOffset, expect, update); } //以原子方式將當前值+1,返回遞增以後的值 public final long getAndIncrement() { return unsafe.getAndAddLong(this, valueOffset, 1L); } //以原子方法將當前值-1,返回遞減以後的值 public final long getAndDecrement() { return unsafe.getAndAddLong(this, valueOffset, -1L); } //以原子方式將當前值+delta返回相加前的值 public final long getAndAdd(long delta) { return unsafe.getAndAddLong(this, valueOffset, delta); } //以原子方式將當前值+1,並返回遞增後的值 public final long incrementAndGet() { return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L; } //以原子方法遞減並返回遞減後的值 public final long decrementAndGet() { return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L; } //以原子方式增長指定的值delta並返回相加後的值 public final long addAndGet(long delta) { return unsafe.getAndAddLong(this, valueOffset, delta) + delta; } //jdk1.8新提供的方法,應用指定函數updateFunction以原子方式更新當前值 public final long getAndUpdate(LongUnaryOperator updateFunction) { long prev, next; do { prev = get(); next = updateFunction.applyAsLong(prev); } while (!compareAndSet(prev, next)); return prev; } //jdk1.8新提供方法,以原子方式應用單元運算更新當前值,返回更新以後的值 public final long updateAndGet(LongUnaryOperator updateFunction) { long prev, next; do { prev = get(); next = updateFunction.applyAsLong(prev); } while (!compareAndSet(prev, next)); return next; } //jdk1.8新提供方法,以原子方式應用一個雙元(當前值和x)運算函數完成當前值更新,返回更新前的值 public final long getAndAccumulate(long x, LongBinaryOperator accumulatorFunction) { long prev, next; do { prev = get(); next = accumulatorFunction.applyAsLong(prev, x); } while (!compareAndSet(prev, next)); return prev; } //jdk1.8新提供方法,以原子方式應用一個雙元(當前值和x)運算函數完成當前值更新,返回更新後的值 public final long accumulateAndGet(long x, LongBinaryOperator accumulatorFunction) { long prev, next; do { prev = get(); next = accumulatorFunction.applyAsLong(prev, x); } while (!compareAndSet(prev, next)); return next; } //字符串形式 public String toString() { return Long.toString(get()); } //當前值轉long public int intValue() { return (int)get(); } //獲取當前最新值,一個反作用是能夠阻塞等待AtomicLong後臺更新操做完成 public long longValue() { return get(); } //當前值轉float public float floatValue() { return (float)get(); } //當前值轉double public double doubleValue() { return (double)get(); }
下面咱們選取incrementAndGet方法進行分析瞭解下AtomicLong實現long變量操做原子性的原理。安全
incrementAndGet方法數據結構
incrementAndGet方法以原子方式將當前值加1,返回遞增後的值,下面是該方法的源碼:多線程
public final long incrementAndGet() { return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L; }
分析源碼可知方法內部經過調用unsafe.getAndAddLong(this,valueOffset,1L)+1L實現對long的原子加減和返回,下面咱們進入Unsafe類的getAndAddLong方法源碼;app
public final long getAndAddLong(Object var1, long var2, long var4) { long var6; do { var6 = this.getLongVolatile(var1, var2); } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4)); return var6; }
分析這段代碼咱們知道,Unsafe基本就是採用循環+cas的方式保證AtomicLong的原子操做。在方法裏面首先在一個循環裏面根據傳入的AtomicLong對象和它指定的版本號valueOffset去獲取對應版本的long值,而後與當前最新版本號進行比較,若是版本號相等那麼AtomicLong的value+1,返回AtomicLong更新前的volatile value,所以方法unsafe.getAndAddLong(this, valueOffset, 1L)+1就是一個volatile修飾的long類型變量+1也是原子性操做且線程安全。函數