Java並發之原子變數與volatile

來源:互聯網
上載者:User

標籤:

我們知道在並發編程中,多個線程共用某個變數或者對象時,必須要進行同步。同步的包含兩層作用:1)互斥訪問;2)可見度;也就是多個線程對共用的變數互斥地訪問,同時線程對共用變數的修改必須對其他線程可見,也就是所有線程訪問到的都是最新的值。

1. volatile變數和引用

volatile的作用是:保證可見度,但是沒有互斥訪問語義。volatile能夠保證它修飾的引用以及引用的對象的可見度,volatile不僅保證變數或者引用對所有訪問它的線程的可見度,同時能夠保證它所引用的對象對所有訪問它的線程的可見度。volatile的使用要求滿足下面的兩個條件:

1)對變數或者引用的讀寫操作不依賴於變數或者引用的當前值(如果只有特定的單個線程修改共用變數,那麼修改操作也是可以依賴於當前值);

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

volatile最常見的錯誤使用情境是使用volatile來實現並發 i++; 錯誤的原因是,該操作依賴於 i 變數的當前值,他是在 i 變數的當前值的基礎上加一,所以說他依賴於 i 的當前值。多個線程執行 i++; 會丟失更新。比如兩個線程同時讀到 i 的當前值8,都進行加一,然後寫回,最終 i 的結果是 9,而不是我們期待的10,丟失了更新。那麼原子變數的引入就是針對volatile的這個缺陷的!!!原子變數的修改操作允許它依賴於當前值,所以說”原子變數“是比volatile的語義稍微強化一點!他不僅具有volatile的可見度,同時對原子變數的修改可以依賴於當前值。

2. 原子變數和原子引用

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

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 asmall number of hardware instructions (on typical machines).  Compilersare 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();   // 加1public void decrement();  // 減1public long sum();            // 求和public void reset();          // 重設0public 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();    }

 

Java並發之原子變數與volatile

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.