AtomicInteger等對象出現的目的主要是爲了解決在多線程環境下變量計數的問題,例如經常使用的i++,i--操做,它們不是線程安全的,AtomicInteger引入後,就沒必要在進行i++和i--操做時,進行加鎖操做,在咱們平常工做中,有不少業務場景須要在多線程環境下進行變量的計數:訂單數統計、訪問量統計、累計相應時長統計等。html
demo 源碼:https://github.com/mantuliu/javaAdvancejava
下面咱們先分析一下AtomicInteger的源代碼。經過源碼分析咱們知道,AtomicInteger的核心就是一個CAS算法(CompareAndSwap),比較並交換算法,此算法是由unsafe的底層代碼實現,它是一個原子的操做,原理就是:若是內存中的實際值與update值相同,則將實際值更新爲expect值,反之則返回失敗,由上層系統循環獲取實際值後,再次調用此CAS算法:git
/* * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.atomic; import sun.misc.Unsafe; /** * An {@code int} value that may be updated atomically. See the * {@link java.util.concurrent.atomic} package specification for * description of the properties of atomic variables. An * {@code AtomicInteger} is used in applications such as atomically * incremented counters, and cannot be used as a replacement for an * {@link java.lang.Integer}. However, this class does extend * {@code Number} to allow uniform access by tools and utilities that * deal with numerically-based classes. * * @since 1.5 * @author Doug Lea */ 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;//value值的偏移地址 static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile int 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;//返回value值 } /** * Sets to the given value. * * @param newValue the new value */ public final void set(int newValue) { value = newValue;//設置新值,由於沒有判斷oldvalue,因此此操做是非線程安全的 } /** * 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);//與set操做效果同樣,只是採用的是unsafe對象中經過偏移地址來設置值的方式 } /** * 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) {//原子操做,設置新值,返回老值 for (;;) { int current = get(); if (compareAndSet(current, newValue))//經過CAS算法,比較current的值和實際值是否一致,若是一致則設置爲newValue return current; } } /** * 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 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>May <a href="package-summary.html#Spurious">fail spuriously</a> * and does not provide ordering guarantees, so is only rarely an * appropriate alternative to {@code compareAndSet}. * * @param expect the expected value * @param update the new value * @return true if successful. */ public final boolean weakCompareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } /** * Atomically increments by one the current value. * * @return the previous value */ public final int getAndIncrement() {//i++操做 for (;;) { int current = get();//獲取當前值 int next = current + 1;//當前值+1 if (compareAndSet(current, next))//比較current值和實際的值是否一致,如不一致,則繼續循環 return current; } } /** * Atomically decrements by one the current value. * * @return the previous value */ public final int getAndDecrement() { for (;;) { int current = get(); int next = current - 1; if (compareAndSet(current, next)) return current; } } /** * Atomically adds the given value to the current value. * * @param delta the value to add * @return the previous value */ public final int getAndAdd(int delta) {//例如:當咱們統計接口的響應時間時,能夠利用此方法 for (;;) { int current = get(); int next = current + delta; if (compareAndSet(current, next)) return current; } } /** * Atomically increments by one the current value. * * @return the updated value */ public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } } /** * Atomically decrements by one the current value. * * @return the updated value */ public final int decrementAndGet() { for (;;) { int current = get(); int next = current - 1; if (compareAndSet(current, next)) return next; } } /** * Atomically adds the given value to the current value. * * @param delta the value to add * @return the updated value */ public final int addAndGet(int delta) { for (;;) { int current = get(); int next = current + delta; if (compareAndSet(current, next)) return next; } } /** * Returns the String representation of the current value. * @return the String representation of the current value. */ 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(); } }
下面,咱們爲四種狀況(同步關鍵字、ReentrantLock公平鎖和非公平鎖、AtomicInteger)作一下性能對比分析,當咱們看到上面的代碼分析後,咱們判斷AtomicInteger應該比加鎖的方式快,可是實驗的結果代表,AtomicInteger只比ReentrantLock加公平鎖的狀況快幾十倍,比其它兩種方式略慢一些。github
四個demo都用100個線程來循環模擬下單60秒鐘:算法
demo Lesson8SyncIntPerform:在使用同步關鍵字加鎖的狀況下100個線程循環下單數爲:677337556windows
demo Lesson8SyncIntPerform:在使用同步關鍵字加鎖的狀況下100個線程循環下單數爲:755994691安全
demo Lesson8AtomicIntPerform:在使用AtomicInteger的狀況下100個線程循環下單數爲:562359607性能優化
demo Lesson8AtomicIntPerform:在使用AtomicInteger的狀況下100個線程循環下單數爲:575367967多線程
demo Lesson8LockIntPerform:在使用ReentrantLock加非公平鎖的狀況下100個線程循環下單數爲:857239882app
demo Lesson8LockIntPerform:在使用ReentrantLock加非公平鎖的狀況下100個線程循環下單數爲:860364303
demo Lesson8LockFairIntPerform:在使用ReentrantLock加公平鎖的狀況下100個線程循環下單數爲:19153640
demo Lesson8LockFairIntPerform:在使用ReentrantLock加公平鎖的狀況下100個線程循環下單數爲:19076567
上面的實驗結果代表,在jdk1.6及後續的版本中(本實驗的jdk版本是1.7,操做系統爲windows操做系統),已經對於synchronized關鍵字的性能優化了不少,已經和ReentrantLock的性能差很少,加鎖的效果比不加鎖時使用AtomicInteger性能效果還要略好一些,可是公平鎖的性能明顯下降,其它三種狀況下的性能是公平鎖性能的幾十倍以上,這和公平鎖每次試圖佔有鎖時,都必須先要進等待隊列,按照FIFO的順序去獲取鎖,所以在咱們的實驗情景下,使用公平鎖的線程進行了頻繁切換,而頻繁切換線程,性能必然會降低的厲害,這也告誡了咱們在實際的開發過程當中,在須要使用公平鎖的情景下,務必要考慮線程的切換頻率。