Java高併發16-LongAdder類源碼解析(下)

一、複習

  • 上次連載簡單的介紹了其他函數的作用以及功能

二、完整的LongAdder類源碼

package com.ruigege.AtomicOperationClass4;
import java.util.function.LongBinaryOperator;

import sun.misc.Unsafe;

@sun.misc.Contended public class LongAdderTest {
 
 volatile long value;
 
 public LongAdderTest(long value) {
  this.value = value;
 }

 private static final Unsafe unsafe;
 private static final long valueOffset;
 static {
  try {
   unsafe = Unsafe.getUnsafe();
   Class<?> ak = Cell.class;
   valueOffset = unsafe.objectFieldOffset(ak.getDeclaredField("value"));
  }catch(Exception e) {
   throw new Error(e);
  }
 }
 
 final long cas(long cmp,long val) {
  return unsafe.compareAndSwapLong(this,valueOffset,cmp,val);
 }
 
 public long sum() {
  Cell[] as = cells;
  long sum = base;
  if(as != null) {
   for (int i=0;i<as.length;i++) {
    if(Cell[i] != null) {
     sum += Cell[i].value;
    }
   }
  }
 }
 
 public void reset() {
  Cell[] as = cells;
  base = 0L;
  if(as != null) {
   for (int i=0;i<as.length;i++) {
    if(as[i] != null) {
     as[i].value = 0L;
    }
   }
  }
 }
 
 public long sunThenReset() {
  Cell[] as = cells;
  int sum = base;
  if(as != null) {
   for (int i=0;i<as.length;i++) {
    if(as[i] != null) {
     sum += as[i];
     as[i] = 0L;
     
    }
   }
  }
 }
 
 public void add(long x) {
  Cell[] as;
  long b,v;
  int m;
  Cell a;
  if((as = cells) != null) || !caseBase(b=base,b+x)){
   //如果as是一個空數組,也就是第一個是false,那麼會去判斷第二個,第二個函數,其實就是一個CAS操作,
   //空數組就意味着沒有初始化數組,這個LongAdder實例,裏面就一個base變量,也就是說併發量太小了,不足以
   //初始化數組,這時這個實例就是一個AtomicLong實例,沒有區別,第二個判斷就是給base賦值爲最新的增長量
   //如果cas失敗了,那麼就進入到下面的判斷語句中
   boolean uncontended = true;
   if(as==null || (m = as.length-1)<0 || (a = as[getProbe() & m]) == null || !(uncontended=a.cas(v=a.value,v+x))){
    /**
     * 四個判斷條件,逐步深入
     * (1)如果數組爲空,繼續執行判斷體內;不爲空,看第二個條件
     * (2)如果數組中元素爲0,繼續執行判斷體內;不少於1個,看第三個條件
     * (3)getProbe()函數用於獲取獲取當前線程中變量threadLocalRandomProbe的值,這個值一開始爲0,在接下
     * 來的判斷體中會進行初始化,並且當前線程通過分配的Cell元素的cas函數來保證對Cell元素value值更新的原子性。
     * 這第三個就是爲了找到一個數組中的Cell變量,我們暫且看到是0&m,那就是0了,也就是啊as[0]賦值給a,並且如果是空的,則直接
     * 進入到判斷執行體;如果不爲空,接着看第四個判斷條件
     * (4)第四個判斷條件就是剛纔的那個a使用CAS操作來更新值,如果更新失敗了就進入到執行體
     */

    longAccumulate(x,null,uncontended);
   }
  }
 }
 
 final boolean casBase(long cmp,long val) {
  return UNSAFE.compareAndSwapLong(this,BASE,cmp,val);
 }
 
 final void longAccumulate(long x,LongBinaryOperator fn,boolean wasUncontended) {
  //初始化當前線程的變量threadLocalRandomProbe的值
  //代碼6
  int h;
  if(h = getProbe() == 0) {
   ThreadLocalRandom.current();//獲取一個隨機數的實例
   h = getProbe();//初始化
   //這個變量在計算當前線程應該被分配到cells數組的哪一個Cell元素的時候會用到
   wasUncontended = true;//contend爭奪,辯稱
  }
  
  boolean collide = false;//collide衝突,碰撞
  for(;;) {
   Cell[] as;Cell a;int n;long v;
   if((as=cells) != null && (n=as.length)>0) {//當前線程調用了add方法並且根據當前線程的隨機數threadLocalRandomProbe和cells元素的個數計算要訪問的Cell元素下
    //下標,然後如果發現對應下標元素的值爲null,則新增一個cell元素到cells數組,並且在將其提娜佳到cells數組之前要競爭設置cellsBusy爲1
    if((a=as[(n-1) &h]) == null) {
     if(cellsBusy == 0) {
      Cell r = new Cell(x);
      if(cellsBusy == 0 && casCellsBusy()) {
       boolean created = false;
      }
      try {
       Cell[] rs;int m,j;
       if((rs=cells) != null && (m=rs.length)>0 && rs[j = (m-1) &h] == null) {
        rs[j] = r;
        created = true;
       }
      }finally {
       cellsBusy = 0;
      }
      if(created) {
       break;
      }
      continue;
     }
    }
    collide = false;
   }else if(!wasUntended) {
    wasUncontended = true;
   }else if(a.cas(v=a.value,((fn==null)?v+x : fn.applyAsLong(v,x)))) {
    break;
   }else if(n >= NCPU || cells != as) {
    collide = false;
   }else if(!collide) {
    collide = ture;
   }else if(cellsBusy == 0 && casCellsBusy()) {
    try {
     if(cells == as) {
      Cell[] rs = new Cell[n<<1];
      for(int i=0;i<n;++i) {
       rs[i] = as[i];
      }
      cells = rs;
     }
    }finally {
     cellsBusy = 0;
    }
    collide = false;
    continue;
   }
   h = advanceProbe(h);
  }
  //代碼14,這裏進行的是cells數組初始化
  /**
   * 1.cellsBusy是一個標示,成員變量,用來實現自旋鎖,狀態值只有0和1,當創建Cell元素,擴容Cell數組或者
   * 初始化數組,使用CAS操作來保證同時只有一個線程可以進行其中之一操作
   * 2.爲0的時候說明cells數組沒有被初始化或者擴容,也沒有新建Cell元素;爲1說明cells數組在被初始化或者擴容,或者當前新建了Cell元素;通過CAS操作來進行0或1狀態切換,這裏使用了casCellBusy函數
   * 3.假設當前線程通過CAS設置cellsBusy爲1,則當前線程開始初始化操作,那麼這個時候其他線程就不能進行擴容了。
   */

  else if(cellsBusy == 0 && cells == as && casCellsBusy()){
   boolean init = false;
   try {
    //因爲在多線程下,所以有必要再次判斷一下
    if(cells == as) {
     //初始化數組容量爲2
     Cell[] rs = new Cell[2];
     //h&l用來計算當前線程應該訪問cell數組的哪個位置
     //也就是使用當前線程的threadLocalRandomProbe變量值和cells數組元素個數-1做與運算
     rs[h&l] = new Cell(x);
     cells = rs;
     //標示數組已經完成初始化
     init = true;
    }
   }finally {
    //最後重置了cellBusys標記,這裏沒有使用CAS操作,但是卻是線程安全的,因爲該變量是一個
    //volatile變量,保證了內存可見性,另外擴容後的cells數組裏面除了包含複製過來的元素外,還包含其他元素,這些元素的值
    //都是null,
    cellsBusy = 0;
   }
   if(init) {
    break;
   }
  }else if(casBase(v = base,((fn == null) ? v+x:fn.applyAsLong(v,x)))) {
   break
  }
  
 }
}

三、源碼:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章