Java併發編程-原子類實現

前言

爲了研究Java對原子類的實現,從AtomicInteger類開始,分析Java若是對原子操做的實現。併發

什麼是原子操做?

原子操做是指不會被線程調度機制打斷的操做;這種操做一旦開始,就一直運行到結束,中間不會有任何上下文的切換。
注:原子操做能夠是一個步驟,也能夠是多個操做步驟,可是其順序不能夠被打亂,也不能夠被切割只執行其中的一部分。工具

源碼分析:

首先從AtomicInteger類的屬性聊起:源碼分析

// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
private volatile int value;

該類共有三個成員屬性。this

  • unsafe:該類是JDK提供的能夠對內存直接操做的工具類。
  • valueOffset:該值保存着AtomicInteger基礎數據的內存地址,方便unsafe直接對內存的操做。
  • value:保存着AtomicInteger基礎數據,使用volatile修飾,能夠保證該值對內存可見,也是原子類實現的理論保障。

再談靜態代碼塊(初始化)線程

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

該過程實際上就是計算成員變量value的內存偏移地址,計算後,能夠更直接的對內存進行操做。
瞭解核心方法compareAndSet(int expect,int update):code

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

在該方法中調用了unsafe提供的服務:對象

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

下面看看這個類在JDK中是如何實現的:內存

jboolean sun::misc::Unsafe::compareAndSwapInt (jobject obj, jlong offset,jint expect, jint update)  {  
  jint *addr = (jint *)((char *)obj + offset); //1
  return compareAndSwap (addr, expect, update);
}  

static inline bool compareAndSwap (volatile jlong *addr, jlong old, jlong new_val)    {    
  jboolean result = false;    
  spinlock lock;    //2
  if ((result = (*addr == old)))    //3
    *addr = new_val;    //4
  return result;  //5
}
  1. 經過對象地址和value的偏移量地址,來計算value的內存地址。
  2. 使用自旋鎖來處理併發問題。
  3. 比較內存中的值與調用方法時調用方所期待的值。
  4. 若是3中的比較符合預期,則重置內存中的值。
  5. 若是成功置換則返回true,不然返回false;

綜上所述:compareAndSet的實現依賴於兩個條件:rem

  • volatile原語:保證在操做內存的值時,該值的狀態爲最新的。(被volatile所修飾的變量在讀取值時都會從變量的地址中讀取,而不是從寄存器中讀取,保證數據對全部線程都是可見的)
  • Unsafe類:經過該類提供的功能,能夠直接對內存進行操做。

瞭解常見操做getAndIncrement():get

return unsafe.getAndAddInt(this, valueOffset, 1);
}

一樣使用unsafe提供的方法:

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);//1
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));//2
    return var5;
}
 
//getIntVolatile方法native實現
jint sun::misc::Unsafe::getIntVolatile (jobject obj, jlong offset)    
{    
  volatile jint *addr = (jint *) ((char *) obj + offset);    //3
  jint result = *addr;    //4
  read_barrier ();    //5
  return result;    //6
}  
inline static void read_barrier(){
  __asm__ __volatile__("" : : : "memory");
}
  1. 經過volatile方法獲取當前內存中該對象的value值。
  2. 計算value的內存地址。
  3. 將值賦值給中間變量result。
  4. 插入讀屏障,保證該屏障以前的讀操做後後續的操做可見。
  5. 返回當前內存值
  6. 經過compareAndSwapInt操做對value進行+1操做,若是再執行該操做過程當中,內存數據發生變動,則執行失敗,但循環操做直至成功。
相關文章
相關標籤/搜索