lesson8:AtomicInteger源碼解析及性能分析

    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的順序去獲取鎖,所以在咱們的實驗情景下,使用公平鎖的線程進行了頻繁切換,而頻繁切換線程,性能必然會降低的厲害,這也告誡了咱們在實際的開發過程當中,在須要使用公平鎖的情景下,務必要考慮線程的切換頻率。

相關文章
相關標籤/搜索