咱們知道在併發編程中,多個線程共享某個變量或者對象時,必需要進行同步。同步的包含兩層做用: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則同時保證可見性和互斥性(原子性)。