能夠原子性更新的 Integer 值,固然這個類並不能徹底替代 Integer 對象。html
使用起來仍是很方便的。java
好比說咱們定義一個計數器,使用 AtomicInteger 能夠同時兼顧性能與併發安全。程序員
import java.util.concurrent.atomic.AtomicInteger; /** * @author binbin.hou * @since 1.0.0 */ public class Counter { private AtomicInteger c = new AtomicInteger(0); /** * 遞增 */ public void increment() { c.getAndIncrement(); } /** * 獲取值 * @return 值 */ public int value() { return c.get(); } public static void main(String[] args) throws InterruptedException { final Counter counter = new Counter(); //1000 threads for(int i = 0; i < 100 ; i++) { new Thread(new Runnable() { public void run() { counter.increment(); } }).start(); } Thread.sleep(1000); System.out.println("Final number (should be 100): " + counter.value()); } }
日誌輸出:緩存
Final number (should be 100): 100
可惡!這個類使用起來居然這麼方便。安全
那麼李大狗是如何實現的呢?併發
public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; }
繼承自 Number 類,實現了序列化接口。app
這裏初始化了 unsafe 變量,用於後面使用 CAS 作變量更新。jvm
// 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;
這是一個 native 方法,換言之就是直接調用的操做系統,獲取到 value 變量的內存偏移量信息。ide
public native long objectFieldOffset(Field var1);
平淡無奇的構造器,用來初始化 value。性能
固然也能夠不指定,不指定的時候默認值是什麼呢?
我想各位讀者確定都清楚,不清楚的能夠留言區懺悔一下。
/** * Creates a new AtomicInteger with the given initial value. * * @param initialValue the initial value */ public AtomicInteger(int initialValue) { value = initialValue; } /** * Creates a new AtomicInteger with initial value {@code 0}. */ public AtomicInteger() { }
/** * Gets the current value. * * @return the current value */ public final int get() { return value; } /** * Sets to the given value. * * @param newValue the new value */ public final void set(int newValue) { value = newValue; } /** * Returns the String representation of the current value. * @return the String representation of the current value */ public String toString() { return Integer.toString(get()); } /** * Returns the value of this {@code AtomicInteger} as an {@code int}. */ public int intValue() { return get(); } /** * Returns the value of this {@code AtomicInteger} as a {@code long} * after a widening primitive conversion. * @jls 5.1.2 Widening Primitive Conversions */ public long longValue() { return (long)get(); } /** * Returns the value of this {@code AtomicInteger} as a {@code float} * after a widening primitive conversion. * @jls 5.1.2 Widening Primitive Conversions */ public float floatValue() { return (float)get(); } /** * Returns the value of this {@code AtomicInteger} as a {@code double} * after a widening primitive conversion. * @jls 5.1.2 Widening Primitive Conversions */ public double doubleValue() { return (double)get(); }
這兩個方法和普通類中的 getter/setter等並無區別,此處不作過多解釋。
爲何 AtomicInteger 能保持原子性呢?
咱們一塊兒來看一下是如何基於 Unsafe 實現原子性的?
/** * Eventually sets to the given value. * * @param newValue the new value * @since 1.6 */ public final void lazySet(int newValue) { unsafe.putOrderedInt(this, valueOffset, newValue); }
最終會把值設置爲給定的值。這是什麼意思?我直接懵了。
其實這個是相對的,咱們前面說過,volatile 修飾的變量,修改後能夠保證線程間的可見性。可是這個方法,修改後並不保證線程間的可見性。
這和之前在網上看到的可不同,不是說好的 AtomicXXX 都是基於 volatile+cas 實現的嗎?這裏爲何要反其道而行之呢?
實際上是爲了性能,lazySet 有本身的應用場景。
高級程序員都知道 volatile 能夠保證變量在線程間的可見性,可是這裏再問一句,不使用 volatile 修飾就沒法保證可見性了嗎?
事實上,這裏徹底能夠不用 volatile 變量來修飾這些共享狀態,
由於訪問共享狀態以前先要得到鎖, Lock.lock()方法可以得到鎖,而得到鎖的操做和volatile變量的讀操做同樣,會強制使CPU緩存失效,強制從內存讀取變量。
底層也是經過加內存屏障實現的。
而lazySet()優化原理,就是在不須要讓共享變量的修改馬上讓其餘線程可見的時候,以設置普通變量的方式來修改共享狀態,能夠減小沒必要要的內存屏障,從而提升程序執行的效率。
這個討論能夠參考 stackoverflow 的問題 AtomicInteger lazySet vs. set
/** * Atomically sets to the given value and returns the old value. * * @param newValue the new value * @return the previous value */ public final int getAndSet(int newValue) { return unsafe.getAndSetInt(this, valueOffset, newValue); }
這個方法實現以下:
public final int getAndSetInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var4)); return var5; }
實際上就是咱們常說的 volatile + CAS 實現。
jdk 將 CAS 這個方法暴露給了開發者,不過作了一層封裝,讓 unsafe 類對使用者不可見。
compareAndSwapInt 這個方法是一個 native 方法,此處不作深刻。其餘的方法不少都大同小異,因此咱們再也不贅述。
ps: 很煩,native 方法直接看源碼就會變得很麻煩,之後有時間研究下 openJdk 之類的。
/** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * * @param expect the expected value * @param update the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
這個方法我以爲也頗有趣,弱比較?拿泥搜來。
/** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * * <p><a href="package-summary.html#weakCompareAndSet">May fail * spuriously and does not provide ordering guarantees</a>, so is * only rarely an appropriate alternative to {@code compareAndSet}. * * @param expect the expected value * @param update the new value * @return {@code true} if successful */ public final boolean weakCompareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
咱們發現這兩個方法在 jdk1.8 中其實是沒有差別的。
底層調用的都是同一個方法:
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
那區別是什麼呢?
因而我就去查了一下,JDK1.9之前,二者底層實現是同樣的,並無嚴格區分。
JDK 1.9提供了Variable Handles的API,主要是用來取代java.util.concurrent.atomic包以及sun.misc.Unsafe類的功能。
Variable Handles須要依賴jvm的加強及編譯器的協助,即須要依賴java語言規範及jvm規範的升級。
VarHandle中compareAndSet和compareAndSet的定義以下:
(1)compareAndSet(Object... args)
Atomically sets the value of a variable to the newValue with the memory semantics of set(java.lang.Object...) if the variable's current value, referred to as the witness value, == the expectedValue, as accessed with the memory semantics of getAcquire(java.lang.Object...).
(2)weakCompareAndSet(Object... args)
Possibly atomically sets the value of a variable to the newValue with the memory semantics of setVolatile(java.lang.Object...) if the variable's current value, referred to as the witness value, == the expectedValue, as accessed with the memory semantics of getVolatile(java.lang.Object...).
weakCompareAndSet的描述多了一個單詞Possibly,可能的。
weakCompareAndSet有可能不是原子性的去更新值,這取決於虛擬機的實現。
@HotSpotIntrinsicCandidate
標註的方法,在HotSpot中都有一套高效的實現,該高效實現基於CPU指令,運行時,HotSpot維護的高效實現會替代JDK的源碼實現,從而得到更高的效率。
也就是說HotSpot可能會手動實現這個方法。
@PolymorphicSignature @HotSpotIntrinsicCandidate public final native boolean compareAndSet(Object... var1); @PolymorphicSignature @HotSpotIntrinsicCandidate public final native boolean weakCompareAndSet(Object... var1);
其實這個方法和上面的 lazySet 有殊途同歸之妙。
咱們對 AtomicInteger 源碼進行了初步的分析,底層也確實是依賴 volatile+CAS 實現。
不過發現了兩個有趣的實現:weakCompareAndSet 和 lazySet。
看起來反其道而行之,實際上都是出於更高的性能考慮。
文中不少方法都是 native 實現,這讓咱們讀起來不夠盡興,說到底這個世界上本沒有高級語言,只有C語言,和對C語言的封裝。
但願本文對你有幫助,若是有其餘想法的話,也能夠評論區和你們分享哦。
各位極客的點贊收藏轉發,是老馬寫做的最大動力!