Java併發之原子變量和原子引用與volatile

咱們知道在併發編程中,多個線程共享某個變量或者對象時,必需要進行同步。同步的包含兩層做用:1)互斥訪問(原子性);2)可見性;也就是多個線程對共享的變量互斥地訪問,同時線程對共享變量的修改必須對其餘線程可見,也就是全部線程訪問到的都是最新的值。html

1. volatile變量和volatile引用java

volatile的做用是:保證可見性,可是沒有互斥訪問語義(原子性語義)。volatile可以保證它修飾的引用以及引用的對象的可見性,volatile不只保證變量或者引用對全部訪問它的線程的可見性,同時可以保證它所引用的對象對全部訪問它的線程的可見性。volatile的使用要求知足下面的兩個條件編程

1)對變量或者引用的寫操做不依賴於變量或者引用的當前值(若是隻有特定的單個線程修改共享變量,那麼修改操做也是能夠依賴於當前值);api

2)該變量或者引用沒有包含在其它的不變式條件中;數組

volatile最多見的錯誤使用場景是使用volatile來實現併發 i++; 錯誤的緣由是,該操做依賴於 i 變量的當前值,他是在 i 變量的當前值的基礎上加一,因此說他依賴於 i 的當前值。多個線程執行 i++; 會丟失更新。好比兩個線程同時讀到 i 的當前值8,都進行加一,而後寫回,最終 i 的結果是 9,而不是咱們期待的10,丟失了更新。那麼原子變量的引入就是針對volatile的這個缺陷的!!!原子變量的修改操做容許它依賴於當前值因此說」原子變量「是比volatile的語義稍微強化一點!他不只具備volatile的可見性,同時對原子變量的修改能夠依賴於當前值併發

2. 原子變量和原子引用app

從Java 1.5開始引入了原子變量和原子引用:ide

java.util.concurrent.atomic.AtomicBoolean函數

java.util.concurrent.atomic.AtomicInteger性能

java.util.concurrent.atomic.AtomicLong

java.util.concurrent.atomic.AtomicReference

以及他們對應的數組:

java.util.concurrent.atomic.AtomicIntegerArray

java.util.concurrent.atomic.AtomicLongArray

java.util.concurrent.atomic.AtomicReferenceArray

原子變量和引用都是使用compareAndSwap(CAS指令)來實現:依賴當前值的原子修改的。

並且他們的實現都是使用volatile和Unsafe:volatile保證可見性,而Unsafe保證原子性

咱們能夠稍微分析下AtomicReference的源碼:

public class AtomicReference<V> implements java.io.Serializable {
    private static final long serialVersionUID = -1848883965231344442L;

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicReference.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile V value;

    /**
     * Creates a new AtomicReference with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicReference(V initialValue) {
        value = initialValue;
    }

    /**
     * Creates a new AtomicReference with null initial value.
     */
    public AtomicReference() {
    }

    /**
     * Gets the current value.
     *
     * @return the current value
     */
    public final V get() {
        return value;
    }

    /**
     * Sets to the given value.
     *
     * @param newValue the new value
     */
    public final void set(V newValue) {
        value = newValue;
    }

    /**
     * Eventually sets to the given value.
     *
     * @param newValue the new value
     * @since 1.6
     */
    public final void lazySet(V newValue) {
        unsafe.putOrderedObject(this, valueOffset, newValue);
    }

    /**
     * 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(V expect, V update) {
        return unsafe.compareAndSwapObject(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(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
    }

    /**
     * Atomically sets to the given value and returns the old value.
     *
     * @param newValue the new value
     * @return the previous value
     */
    @SuppressWarnings("unchecked")
    public final V getAndSet(V newValue) {
        return (V)unsafe.getAndSetObject(this, valueOffset, newValue);
    }

    /**
     * Atomically updates the current value with the results of
     * applying the given function, returning the previous value. The
     * function should be side-effect-free, since it may be re-applied
     * when attempted updates fail due to contention among threads.
     *
     * @param updateFunction a side-effect-free function
     * @return the previous value
     * @since 1.8
     */
    public final V getAndUpdate(UnaryOperator<V> updateFunction) {
        V prev, next;
        do {
            prev = get();
            next = updateFunction.apply(prev);
        } while (!compareAndSet(prev, next));
        return prev;
    }
......

咱們能夠看到使用了: private volatile V value; 來保證 value 的可見性;

同時:

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicReference.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

這段代碼的意思是:得到AtomicReference<V>實例化對象中的 value 屬性的在該對象在堆內存的偏移 valueOffset 位置,而:

unsafe.compareAndSwapObject(this, valueOffset, expect, update);

的做用就是經過比較valueOffset處的內存的值是否爲expect,是的話就更新替換成新值update,這個操做是原子性的。因此volatile保證了可見性,而unsafe保證了原子性。源碼中的UnaryOperator,BinaryOperator等等明顯是模仿C++的,由於Java中沒有函數指針,因此只能使用一元、二元操做對象來實現相應的功能。

3. Unsafe

Unsafe的源碼能夠參見:http://www.docjar.com/html/api/sun/misc/Unsafe.java.html

他的實現主要是經過編譯器,利用CPU的一些原子指令來實現的。

Most methods in this class are very low-level, and correspond to a
small number of hardware instructions (on typical machines).  Compilers
are encouraged to optimize these methods accordingly.

4. LongAdder(加法器)

在jdk 1.8中又引入了進過充分優化的原子變量「加法器」:java.util.concurrent.atomic.LongAdder,它的性能在和其餘原子變量以及volatile變量相比都是最好的,因此在能使用LongAdder的地方就不要使用其它原子變量了。可是LongAdder中並無提供:依賴於當前變量的值來修改的操做。通常用於實現併發計數器是最好的。

LongAdder實現下列接口:

public void add(long x);    // 加 x   
public void increment();   // 加1
public void decrement();  // 減1
public long sum();            // 求和
public void reset();          // 重置0
public String toString() {
        return Long.toString(sum());
    }

    /**
     * Equivalent to {@link #sum}.
     *
     * @return the sum
     */
    public long longValue() {
        return sum();
    }

    /**
     * Returns the {@link #sum} as an {@code int} after a narrowing
     * primitive conversion.
     */
    public int intValue() {
        return (int)sum();
    }

    /**
     * Returns the {@link #sum} as a {@code float}
     * after a widening primitive conversion.
     */
    public float floatValue() {
        return (float)sum();
    }

    /**
     * Returns the {@link #sum} as a {@code double} after a widening
     * primitive conversion.
     */
    public double doubleValue() {
        return (double)sum();
    }

 總結:

就同步語義而言,volatile < 原子變量/原子引用 < lock/synchronized. volatile只保證可見性;原子變量和原子引用保證可見性的同時,利用CAS指令實現了原子修改——「修改操做容許它依賴於當前值」;而lock/synchronized則同時保證可見性和互斥性(原子性)。

相關文章
相關標籤/搜索