jdk源碼->併發->Unsafe&Atomic

synchronized關鍵字相似於java中的悲觀鎖機制,接下來介紹一種java的樂觀鎖機制Unsafe類java

CAS

CAS簡介

CAS全稱是Compare And Swap,即比較交換,它是在併發執行時的一種無鎖思想,其主要包含三個參數:android

/**
*V主內存中的值
*E表示線程中舊的預期值
*N表示新值
**/
CAS(V,E,N)

操做過程能夠描述爲:將主內存中的值與當前線程中變量副本值進行比較,若是相等的,說明在這期間沒有線程修改主內存中的變量值,那麼主內存值改成N,可是若是不相等,說明其餘線程已經操做過了,這時須要從新讀取主內存中的值到線程中來置爲預期值,從新進行這個操做 流程圖以下: 數組

jvm對CAS的支持

jvm中的源碼安全

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
  UnsafeWrapper("Unsafe_CompareAndSwapInt");
  oop p = JNIHandles::resolve(obj);
  jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
  return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END

Linux的X86,Atomic::cmpxchg方法對的實現以下:併發

inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
  int mp = os::is_MP();
  __asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)"
                    : "=a" (exchange_value)
                    : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
                    : "cc", "memory");
  return exchange_value;
}

Unsafe

CAS在java中的實現是Unsafe類,類如其名,不安全,由於Unsafe類提供了直接操做內存的方式app

源碼分析

  • 內存管理,Unsafe類中存在直接操做內存的方法
//分配指定bytes的內存
public native long allocateMemory(long bytes);
//根據給定address從新分配指定bytes內存
public native long reallocateMemory(long address, long bytes);
//釋放內存
public native void freeMemory(long address);
//將指定對象的給定offset偏移量內存塊中的全部字節設置爲固定值
public native void setMemory(Object o, long offset, long bytes, byte value);
//設置給定內存地址的值
public native void putAddress(long address, long x);
//獲取指定內存地址的值
public native long getAddress(long address);
//設置給定內存地址的long值
public native void putLong(long address, long x);
//獲取指定內存地址的long值
public native long getLong(long address);
//設置給定內存地址的byte值
public native void  putByte(long address, byte x);
//獲取指定內存地址的byte值
public native byte  getByte(long address);
//其餘基本數據類型(long,char,float,double,short等)的操做與putByte及getByte相同
//操做系統的內存頁大小
public native int pageSize();
  • 獲取類的對象
//傳入一個對象的class並建立該實例對象,但不會調用構造方法
public native Object allocateInstance(Class cls) throws InstantiationException;
  • 類和對象以及變量的操做
//獲取字段f在實例對象中的偏移量
public native long objectFieldOffset(Field f);
//獲取靜態變量f在class對象中的偏移量
public native long staticFieldOffset(Field f);
//根據靜態變量獲取class對象
public native Object staticFieldBase(Field f);
//得到給定對象偏移量上的int值,所謂的偏移量能夠簡單理解爲指針指向該變量的內存地址,
//經過偏移量即可獲得該對象的變量,進行各類操做
public native int getInt(Object o, long offset);
//設置給定對象上偏移量的int值
public native void putInt(Object o, long offset, int x);
//得到給定對象偏移量上的引用類型的值
public native Object getObject(Object o, long offset);
//設置給定對象偏移量上的引用類型的值
public native void putObject(Object o, long offset, Object x);
//其餘基本數據類型(long,char,byte,float,double)的操做與getInthe及putInt相同
//設置給定對象的int值,使用volatile語義,即設置後立馬更新到內存對其餘線程可見
public native void  putIntVolatile(Object o, long offset, int x);
//得到給定對象的指定偏移量offset的int值,使用volatile語義,總能獲取到最新的int值。
public native int getIntVolatile(Object o, long offset);

//其餘基本數據類型(long,char,byte,float,double)的操做與putIntVolatile及getIntVolatile相同,引用類型putObjectVolatile也同樣。
//與putIntVolatile同樣,但要求被操做字段必須有volatile修飾
public native void putOrderedInt(Object o,long offset,int x);
  • Unsafe實例的獲取方式:
Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
    /**
    *會拋出SecurityException異常的方式:
    *Unsafe un = Unsafe.getUnsafe();
    **/
  • 一個實例:
public class test {

    public  static  void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        // 經過反射獲得theUnsafe對應的Field對象
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        // 設置該Field爲可訪問
        field.setAccessible(true);
        // 經過Field獲得該Field對應的具體對象,傳入null是由於該Field爲static的
        Unsafe unsafe = (Unsafe) field.get(null);
        System.out.println(unsafe);

        //經過allocateInstance直接建立對象
        User user = (User) unsafe.allocateInstance(User.class);

        Class userClass = user.getClass();
        Field name = userClass.getDeclaredField("name");
        Field age = userClass.getDeclaredField("age");
        Field id = userClass.getDeclaredField("id");

        //獲取實例變量name和age在對象內存中的偏移量並設置值
        unsafe.putInt(user,unsafe.objectFieldOffset(age),18);
        unsafe.putObject(user,unsafe.objectFieldOffset(name),"android TV");

        // 這裏返回 User.class,
        Object staticBase = unsafe.staticFieldBase(id);
        System.out.println("staticBase:"+staticBase);

        //獲取靜態變量id的偏移量staticOffset
        long staticOffset = unsafe.staticFieldOffset(userClass.getDeclaredField("id"));
        //獲取靜態變量的值
        System.out.println("設置前的ID:"+unsafe.getObject(staticBase,staticOffset));
        //設置值
        unsafe.putObject(staticBase,staticOffset,"SSSSSSSS");
        //獲取靜態變量的值
        System.out.println("設置後的ID:"+unsafe.getObject(staticBase,staticOffset));
        //輸出USER
        System.out.println("輸出USER:"+user.toString());

        long data = 1000;
        byte size = 1;//單位字節

        //調用allocateMemory分配內存,並獲取內存地址memoryAddress
        long memoryAddress = unsafe.allocateMemory(size);
        //直接往內存寫入數據
        unsafe.putAddress(memoryAddress, data);
        //獲取指定內存地址的數據
        long addrData=unsafe.getAddress(memoryAddress);
        System.out.println("addrData:"+addrData);

        /**
         * 輸出結果:
         sun.misc.Unsafe@6f94fa3e
         staticBase:class geym.conc.ch4.atomic.User
         設置前的ID:USER_ID
         設置前的ID:SSSSSSSS
         輸出USER:User{name='android TV', age=18', id=SSSSSSSS'}
         addrData:1000
         */

    }
}

class User{
    public User(){
        System.out.println("user 構造方法被調用");
    }
    private String name;
    private int age;
    private static String id="USER_ID";

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +'\'' +
                ", id=" + id +'\'' +
                '}';
    }
}
  • 數組操做:
//獲取數組第一個元素的偏移地址
public native int arrayBaseOffset(Class arrayClass);
//數組中一個元素佔據的內存空間,arrayBaseOffset與arrayIndexScale配合使用,可定位數組中每一個元素在內存中的位置
public native int arrayIndexScale(Class arrayClass);
  • CAS操做相關
//第一個參數o爲給定對象,offset爲對象內存的偏移量,經過這個偏移量迅速定位字段並設置或獲取該字段的值
//expected表示當前線程指望值(舊值),x表示要設置的值,下面3個方法都經過CAS原子指令執行操做。
public final native boolean compareAndSwapObject(Object o, long offset,Object expected, Object x); 
public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x);
public final native boolean compareAndSwapLong(Object o, long offset,long expected,long x);
   /**
    *下述是jdk8中新增長的
    **/
 //這是一個CAS操做過程,直到設置成功才退出循環,返回舊值
 public final int getAndAddInt(Object o, long offset, int delta) {
     int v;
     do {
         //將預期值更新爲內存中最新值
         v = getIntVolatile(o, offset);
       //CAS操做直到爲true
     } while (!compareAndSwapInt(o, offset, v, v + delta));
     return v;
 }

 public final long getAndAddLong(Object o, long offset, long delta) {
     long v;
     do {
         v = getLongVolatile(o, offset);
     } while (!compareAndSwapLong(o, offset, v, v + delta));
     return v;
 }

 public final int getAndSetInt(Object o, long offset, int newValue) {
     int v;
     do {
         v = getIntVolatile(o, offset);
     } while (!compareAndSwapInt(o, offset, v, newValue));
     return v;
 }

 public final long getAndSetLong(Object o, long offset, long newValue) {
     long v;
     do {
         v = getLongVolatile(o, offset);
     } while (!compareAndSwapLong(o, offset, v, newValue));
     return v;
 }
 public final Object getAndSetObject(Object o, long offset, Object newValue) {
     Object v;
     do {
         v = getObjectVolatile(o, offset);
     } while (!compareAndSwapObject(o, offset, v, newValue));
     return v;
 }
  • 掛起與恢復
//線程調用該方法,線程將一直阻塞直到超時,或者是中斷條件出現。  
public native void park(boolean isAbsolute, long time);  

//終止掛起的線程,恢復正常.java.util.concurrent包中掛起操做都是在LockSupport類實現的,其底層正是使用這兩個方法,  
public native void unpark(Object thread);
  • 內存屏障
//在該方法以前的全部讀操做,必定在load屏障以前執行完成
public native void loadFence();
//在該方法以前的全部寫操做,必定在store屏障以前執行完成
public native void storeFence();
//在該方法以前的全部讀寫操做,必定在full屏障以前執行完成,這個內存屏障至關於上面兩個的合體功能
public native void fullFence();

Atomic

CAS在java中的應用,主要體如今併發包中的原子操做類(Atomic系列),從jdk1.5開始提供了java.util.concurrent.atomic包。jvm

AtomicInteger

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // 獲取Unsafe
    private static final Unsafe unsafe = Unsafe.getUnsafe();

    //volatile修飾的value變量
    private volatile int value;

    //用來保存value變量在AtomicInteger對象內的內存偏移量
    //私有靜態不可變
    private static final long valueOffset;

    static {
        try {
           //經過unsafe類的objectFieldOffset()方法,獲取value變量在對象內存中的偏移
           //經過該偏移量valueOffset,unsafe類的內部方法能夠獲取到變量value對其進行取值或賦值操做
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    //初始化value的構造函數
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }
    public AtomicInteger() {
    }
   //獲取當前最新值,
    public final int get() {
        return value;
    }
    //設置當前值,具有volatile效果,方法用final修飾是爲了更進一步的保證線程安全。
    public final void set(int newValue) {
        value = newValue;
    }
    //最終會設置成newValue,使用該方法後可能致使其餘線程在以後的一小段時間內能夠獲取到舊值,有點相似於延遲加載
    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }
   //設置新值並獲取舊值,底層調用的是CAS操做即unsafe.compareAndSwapInt()方法
    public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }
   //若是當前值爲expect(當前值指的是value變量),則設置爲update
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    //當前值加1返回舊值,底層CAS操做
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
    //當前值減1,返回舊值,底層CAS操做
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }
   //當前值增長delta,返回舊值,底層CAS操做
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }
    //當前值加1,返回新值,底層CAS操做
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
    //當前值減1,返回新值,底層CAS操做
    public final int decrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
    }
   //當前值增長delta,返回新值,底層CAS操做
    public final int addAndGet(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
    }
   //省略一些不經常使用的方法....
}

AtomicReference

public class AtomicReference<V> implements java.io.Serializable {
    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); }
    }
 /**
  *和AtomicInteger類相比,value從int類型變成了範型指定的類型
  **/
    private volatile V value;

 /**
  *和AtomicInteger類類似,只不過調用的是Unsafe類中操做Object的方法
  **/
public final boolean compareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
public final V getAndSet(V newValue) {
        return (V)unsafe.getAndSetObject(this, valueOffset, newValue);
    }
}

AtomicIntegerArray

public class AtomicIntegerArray implements java.io.Serializable {
    private static final long serialVersionUID = 2862133569453604235L;
    //獲取unsafe類的實例對象
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    //獲取數組第一個元素內存起始地址
    private static final int base = unsafe.arrayBaseOffset(int[].class);
    /**計算數組中第i個元素內存地址增量時,能夠用左移shift(i<<shift) 
      *來代替i*scale,換句話說scale是2的shift次冪,至於爲何要這麼作
      *說是移位運算的效率比乘法效率高
      **/
    private static final int shift;
    //主要操做的數組對象
    private final int[] array;

    static {
        //獲取數組中一個元素佔據的內存空間
        int scale = unsafe.arrayIndexScale(int[].class);
        //判斷是不是2的次冪,不然拋出異常
        if ((scale & (scale - 1)) != 0)
            throw new Error("data type scale not a power of two");
        /**
          *Integer.numberOfLeadingZeros(scale)用來計算int型變量轉換爲二進制後從最高位開始連續零的個數
          *例如:在該類中scale=4(由於一個int類型佔據4個字節)那麼結果能夠直觀的表示爲:00000000 00000000 00000000 00000100 爲29,shift=2
          **/
        shift = 31 - Integer.numberOfLeadingZeros(scale);
    }
    //先越界檢查而後調用byteOffset返回計算後的第i個元素地址
    private long checkedByteOffset(int i) {
        if (i < 0 || i >= array.length) //越界檢查
            throw new IndexOutOfBoundsException("index " + i);
        return byteOffset(i);
    }
    //返回數組第i個元素的位置
    private static long byteOffset(int i) {
        return ((long) i << shift) + base;
    }
    //初始化array的構造方法
    public AtomicIntegerArray(int[] array) {
        this.array = array.clone();
    }
    //得到數組長度
    public final int length() {
        return array.length;
    }
    //先越界檢查肯定偏移量而後getRow
    public final int get(int i) {
        return getRaw(checkedByteOffset(i));
    }
    //Unsafe方法得到volatile變量的值
    private int getRaw(long offset) {
        return unsafe.getIntVolatile(array, offset);
    }
    //Unsafe方法設置volatile變量的值
    public final void set(int i, int newValue) {
        unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
    }
    /**
    *下述方法均爲調用Unsafe類的CAS操做,再也不累述
    **/
    public final int getAndSet(int i, int newValue) {
        return unsafe.getAndSetInt(array, checkedByteOffset(i), newValue);
    }
    public final boolean compareAndSet(int i, int expect, int update) {
        return compareAndSetRaw(checkedByteOffset(i), expect, update);
    }
    private boolean compareAndSetRaw(long offset, int expect, int update) {
        return unsafe.compareAndSwapInt(array, offset, expect, update);
    }
    public final int getAndIncrement(int i) {
        return getAndAdd(i, 1);
    }
    public final int getAndDecrement(int i) {
        return getAndAdd(i, -1);
    }

    public final int getAndAdd(int i, int delta) {
        return unsafe.getAndAddInt(array, checkedByteOffset(i), delta);
    }

    public final int incrementAndGet(int i) {
        return getAndAdd(i, 1) + 1;
    }
    public final int decrementAndGet(int i) {
        return getAndAdd(i, -1) - 1;
    }
    public final int addAndGet(int i, int delta) {
        return getAndAdd(i, delta) + delta;
    }

AtomicIntegerFieldUpdater

public abstract class AtomicIntegerFieldUpdater<T> {
    //靜態工廠方法用來得到AtomicIntegerFieldUpdaterImpl對象
    public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
                                                              String fieldName) {
        return new AtomicIntegerFieldUpdaterImpl<U>
            (tclass, fieldName, Reflection.getCallerClass());
    }
    //只能夠在本類和子類中實例化AtomicIntegerFieldUpdater對象
    protected AtomicIntegerFieldUpdater() {
    }
     /**
      *下述僅列出了部分抽象方法,在私有內部子類中會被重寫
      **/
    public abstract boolean compareAndSet(T obj, int expect, int update);
    public abstract void set(T obj, int newValue);
    public abstract int get(T obj);
     /**
      *下述是私有內部子類的代碼
      **/
     private static final class AtomicIntegerFieldUpdaterImpl<T>
        extends AtomicIntegerFieldUpdater<T> {
        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
        private final long offset;//記錄Field的偏移量
        private final Class<?> cclass;//classLoader的class對象
        private final Class<T> tclass;//field所屬類的class對象
         /**
          *tclass:Field所屬類的class對象
          *fieldName:field的名稱
          *caller:得到類加載器
          **/
        AtomicIntegerFieldUpdaterImpl(final Class<T> tclass,
                                      final String fieldName,
                                      final Class<?> caller) {
            final Field field; //要修改字段的Field對象
            final int modifiers;//修飾符
            try {
                field = AccessController.doPrivileged(
                    new PrivilegedExceptionAction<Field>() {
                        public Field run() throws NoSuchFieldException {
                            //反射得到Field對象
                            return tclass.getDeclaredField(fieldName);
                        }
                    });
                modifiers = field.getModifiers();
                sun.reflect.misc.ReflectUtil.ensureMemberAccess(
                    caller, tclass, null, modifiers);
                ClassLoader cl = tclass.getClassLoader();
                ClassLoader ccl = caller.getClassLoader();
                if ((ccl != null) && (ccl != cl) &&
                    ((cl == null) || !isAncestor(cl, ccl))) {
                    sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
                }
            } catch (PrivilegedActionException pae) {
                throw new RuntimeException(pae.getException());
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
            //判斷是不是int類型
            if (field.getType() != int.class)
                throw new IllegalArgumentException("Must be integer type");
            //判斷是否被volatile修飾
            if (!Modifier.isVolatile(modifiers))
                throw new IllegalArgumentException("Must be volatile type");
            this.cclass = (Modifier.isProtected(modifiers) &&
                           tclass.isAssignableFrom(caller) &&
                           !isSamePackage(tclass, caller))
                          ? caller : tclass;
            this.tclass = tclass;//初始化tclass
            this.offset = U.objectFieldOffset(field);//調用Unsafe方法初始化offset
        }
        /**
         *下面重寫了AtomicIntegerFieldUpdater的抽象方法,利用java多態性調用
         **/
        public final boolean compareAndSet(T obj, int expect, int update) {
            accessCheck(obj);
            return U.compareAndSwapInt(obj, offset, expect, update);
        }
        public final void set(T obj, int newValue) {
            accessCheck(obj);
            U.putIntVolatile(obj, offset, newValue);
        }
        public final int get(T obj) {
            accessCheck(obj);
            return U.getIntVolatile(obj, offset);
        }

CAS的ABA問題

ABA用語言描述就是:某個線程當前的指望值(舊值)爲A,它準備執行CAS操做,但在它執行以前,有其它線程成功執行了兩次CAS操做,第一次將主內存值從A->B,第二次將B->A,這時原來那個線程執行CAS操做,併成功,可是中間的修改過程沒法得知。 該過程可用下圖表示; ide

Atomic包中也提供瞭解決ABA問題的類,接下來介紹該類函數

AtomicStampedReference

CAS操做的成功執行依靠當前線程值和主內存中值的比較,若是相等就能更新,而且這個更新是沒有方向的,也就是說屢次更新的累積效果可能爲0,致使這部分更新信息丟失,這就是ABA問題出現根本緣由。那麼能不能解決這個問題呢,很簡單,既然咱們要修改的字段更新是無方向的,那麼咱們就增長一個字段每更新一次字段朝同一方向變化一次,在cas更新操做的時候就比較這兩個字段都相等才能更新成功,這樣就不會出現ABA問題了,下述AtomicStampedReference類就是按照這個思路實現的:oop

public class AtomicStampedReference<V> {
    /**
      *pair:內部靜態類,封裝了實際操做的變量和時間戳
      **/
    private static class Pair<T> {
        final T reference;
        final int stamp;
        private Pair(T reference, int stamp) {
            this.reference = reference;
            this.stamp = stamp;
        }
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
    }
    //將pair做爲一個對象來進行cas操做
    private volatile Pair<V> pair;

    //構造函數,初始化變量和時間戳
    public AtomicStampedReference(V initialRef, int initialStamp) {
        pair = Pair.of(initialRef, initialStamp);
    }

    public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair;
        return
            expectedReference == current.reference &&
            expectedStamp == current.stamp &&
            ((newReference == current.reference &&
              newStamp == current.stamp) ||
             casPair(current, Pair.of(newReference, newStamp)));
    }

    private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
    //得到pair的偏移量
    private static final long pairOffset =
        objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);

    private boolean casPair(Pair<V> cmp, Pair<V> val) {
        //CAS操做更新pair
        return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
    }

    static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
                                  String field, Class<?> klazz) {
        try {
            //Unsafe操做得到pair的偏移量
            return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
        } catch (NoSuchFieldException e) {
            NoSuchFieldError error = new NoSuchFieldError(field);
            error.initCause(e);
            throw error;
        }
    }
}
相關文章
相關標籤/搜索