LongAdder源碼分析


AtomicLong是作用是對長整形進行原子操作,顯而易見,在java1.8中新加入了一個新的原子類LongAdder,該類也可以保證Long類型操作的原子性,相對於AtomicLongLongAdder有着更高的性能和更好的表現,可以完全替代AtomicLong的來進行原子操作。

AtomicLong的代碼很簡單,下面僅以incrementAndGet()為例,對AtomicLong的原理進行說明。
incrementAndGet()源碼如下:

public final long incrementAndGet() {  
    for (;;) {  
        // 獲取AtomicLong當前對應的long值  
        long current = get();  
        // 將current加1  
        long next = current + 1;  
        // 通過CAS函數,更新current的值  
        if (compareAndSet(current, next))  
            return next;  
    }  
}

// value是AtomicLong對應的long值  
private volatile long value;  
// 返回AtomicLong對應的long值  
public final long get() {  
    return value;  
}

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

compareAndSet()的作用是更新AtomicLong對應的long值。它會比較AtomicLong的原始值是否與expect相等,若相等的話,則設置AtomicLong的值為update。

 AtomicLong更高效的LongAdder  

 AtomicLong的實現方式是內部有個value 變量,當多線程並發自增,自減時,均通過cas 指令從機器指令級別操作保證並發的原子性。

AtomicLong的實現方式是內部有個value 變量,當多線程並發自增,自減時,均通過CAS 指令從機器指令級別操作保證並發的原子性。


 LongAdder是jdk8新增的用於並發環境的計數器,目的是為了在高並發情況下,代替AtomicLong/AtomicInt,成為一個用於高並發情況下的高效的通用計數器。

高並發下計數,一般最先想到的應該是AtomicLong/AtomicInt,AtmoicXXX使用硬件級別的指令 CAS 來更新計數器的值,這樣可以避免加鎖,機器直接支持的指令,效率也很高。但是AtomicXXX中的 CAS 操作在出現線程競爭時,失敗的線程會白白地循環一次,在並發很大的情況下,因為每次CAS都只有一個線程能成功,競爭失敗的線程會非常多。失敗次數越多,循環次數就越多,很多線程的CAS操作越來越接近 自旋鎖(spin lock)計數操作本來是一個很簡單的操作,實際需要耗費的cpu時間應該是越少越好,AtomicXXX在高並發計數時,量的cpu時間都浪費會在 自旋 上了,這很浪費,也降低了實際的計數效率

// jdk1.8的AtomicLong的實現代碼,這段代碼在sun.misc.Unsafe中
// 當線程競爭很激烈時,while判斷條件中的CAS會連續多次返回false,這樣就會造成無用的循環,循環中讀取volatile變量的開銷本來就是比較高的
// 因為這樣,在高並發時,AtomicXXX並不是那么理想的計數方式
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;
}

LongAdder比在高並發時比AtomicLong更高效,這么說有什么依據呢?LongAdder是根據ConcurrentHashMap這類為並發設計的類的基本原理——鎖分段,來實現的,它里面維護一組按需分配的計數單元,並發計數時,不同的線程可以在不同的計數單元上進行計數,這樣減少了線程競爭,提高了並發效率。本質上是用空間換時間的思想,不過在實際高並發情況中消耗的空間可以忽略不計。

現在,在處理高並發計數時,應該優先使用LongAdder,而不是繼續使用AtomicLong。當然,線程競爭很低的情況下進行計數,使用Atomic還是更簡單更直接,並且效率稍微高一些。

 

既要看單線程的執行結果,還要看多線程對他的影響。

 

每次操作時候都要看局部變量是不是等於成員變量(判斷是否沒有別的線程干擾),最后再把局部變量賦值給成員變量完成修改。成員變量的修改都是CAS。 

 

@SuppressWarnings("serial")
abstract class Striped641 extends Number1 {
    static final int NCPU = Runtime.getRuntime().availableProcessors();
    transient volatile Cell[] cells;// cell數組,長度一樣要是2^n,可以類比為jdk1.7的ConcurrentHashMap中的segments數組
    // 累積器的基本值 ,沒有遇到並發的情況,直接使用base,速度更快;
    transient volatile long base;//cas更新
    // 自旋標識,在對cells進行初始化,或者后續擴容時,需要通過CAS操作把此標識設置為1(busy,忙標識,相當於加鎖), 取消busy時可以直接使用cellsBusy = 0,相當於釋放鎖
    transient volatile int cellsBusy;//旋轉鎖
    Striped641() {
    }
    final boolean casBase(long cmp, long val) {//原子更新base
        return UNSAFE.compareAndSwapLong(this, BASE, cmp, val);
    }
    // 使用CAS將cells自旋標識更新為1,更新為0時可以不用CAS(賦值為0肯定是只有一個線程在賦值為0),直接使用cellsBusy就行
    final boolean casCellsBusy() {//原子更新cellsBusy從0到1,以獲取鎖。
        return UNSAFE.compareAndSwapInt(this, CELLSBUSY, 0, 1);
    }

    // 下面這兩個方法是ThreadLocalRandom中的方法,不過因為包訪問關系,這里又重新寫一遍
     
    // probe翻譯過來是探測/探測器/探針這些,不好理解,它是ThreadLocalRandom里面的一個屬性,
    // 不過並不影響對Striped64的理解,這里可以把它理解為線程本身的hash值
    static final int getProbe() {
        return UNSAFE.getInt(Thread.currentThread(), PROBE);
    }
    // 相當於rehash,重新算一遍線程的hash值
    static final int advanceProbe(int probe) {
        probe ^= probe << 13; // xorshift
        probe ^= probe >>> 17;
        probe ^= probe << 5;
        UNSAFE.putInt(Thread.currentThread(), PROBE, probe);//CAS設置當前線程的threadLocalRandomProbe
        return probe;
    }

    //x:要增加的數。fn:執行函數。uncontended=false表示更新失敗了,=true表示沒有這個線程的Cell(不可能是更新成功了,更新成功就進不來這里)。
    final void longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended) {//開始分段更新:1.base更新失敗。2.前面分段更新不行或失敗。
        //新建,更新,擴容,初始化,更新base。
        
        int h;//線程hash值
        // 看下ThreadLocalRandom是否初始化。如果當前線程的threadLocalRandomProbe為0,說明當前線程是第一次進入該方法,
        if ((h = getProbe()) == 0) {// 當前線程hash值=0,
            ThreadLocalRandom.current(); //初始化當前線程的PROBE值不為0,
            h = getProbe();
            wasUncontended = true;//下面的cas語句走不走,還是間隔一個for循環在cas  uncontended=false代表存在爭用,uncontended=true代表不存在爭用。
        }
        
        boolean collide = false; //下面的擴容語句走不走,還是間隔一個for循環在擴容  。collide=true代表cas有沖突,collide=false代表cas無沖突 
        
        for (;;) {
            Cell[] as;//局部變量,線程執行時候,局部變量不會變,成員變量會改變(修改屬性地址不變,重新new地址才改變)。as一進來就賦值后面沒有更改過。
            Cell a;//線程對應的cell,a一進來就賦值后面沒有更改過。 //cellsBusy沒有局部變量,直接使用成員變量。是一個鎖。
            int n;
            long v;
            
            //------------------------------------------重要---多線程時候,在一個線程的周期里面,前一個指令的判斷(被另一個線程修改)現在不一定成立了-----------------------------------------------------//
            /*每次執行真正操作時候,有可能剛才判斷的條件全部不成立了,就要重來,那么剛才判斷條件有什么用:不沖突有用。
            執行成功時候:在鎖住期間,剛才進來的判斷都成立,近似於單線程操作,或者別的操作了,但是不影響現在要操作的條件。*/
            /*多線程同時判斷3個if,有可能一個線程判斷第一個if不成立,但是判斷第二個if時候,第一個if條件又成立了。走到后面的判斷,只能說剛才前面的判斷不成立,現在前面的判斷不一定不成立了。
             所以進入一個if:要看2個判斷,之前判斷是什么,現在判斷是什么,才進入這個if條件 */
            /*casCellsBusy()用於鎖住這個cells,別的線程不能擴容和初始化和新建cell(但是可以cas更新存在的cell)。但是casCellsBusy()前后的條件不一定再次成立了,所以鎖住之后要再次判斷剛才的條件 ,多線程時候上一次的判斷現在不一定成立了*/
            /*局部變量,線程執行時候,局部變量不會變,成員變量會改變(修改屬性地址不變,重新new地址才改變)。as一進來就賦值后面沒有更改過*/
            /*在一個線程里面:1.已經有cells,要么新建(新建時候看是不是空),要么更新(要看是不是原值),要么擴容(要看cells有沒有變化)。2.沒有cells就去初始化(初始話時候再看是不是空)。3.初始化搶不贏就去更新base。*/
            //------------------------------------------重要-----多線程時候,在一個線程的判斷周期里面,前一個指令的判斷(被另一個線程修改)現在不一定成立了---------------------------------------------------//
            
            
            //as == cells,只有初始化和擴容(因為重新new)才不相等,修改值還是相等的。
            //每次重新來,都會重新獲取as和a,as = cells,a = as[(n - 1) & h],並且更新線程hash。
            
            // 1.已經有分段更新了cells!=null 
            if ((as = cells) != null && (n = as.length) > 0) {
                //1.1 沒有這個線程的Cell,新建
                //重新來:1.新建cell時候有人占用cell。2.新建cell時候位置不為空。 
                if ((a = as[(n - 1) & h]) == null) { 
                    if (cellsBusy == 0) { // cellsBusy=1表示有人在修改Cells數組(修改Cell從null到new Cell,擴容,初始化),CAS更新一個已經存在的Cell不用判斷cellsBusy。
                        Cell r = new Cell(x);  //這期間其他線程可以做很多事
                        if (cellsBusy == 0 && casCellsBusy()) {// cellsBusy是0就進來,然后變成cellsBusy=1,別的進不來。
                            
                            //不可能多個線程同時進這里面來。
                            
                            /*鎖住cells,但是前面判斷a=null,現在不一定a=null了,因為在前面判斷到鎖住cells期間cells有可能改變了,並且cellsBusy從0變到1又變到0。所以鎖住之后在判斷是不是空。*/
                            
                            /*每次執行真正操作時候,有可能剛才判斷的條件全部不成立了,就要重來,那么剛才判斷條件有什么用:不沖突有用。 執行成功時候:在鎖住期間,剛才進來的判斷都成立,近似於單線程操作,或者別的操作了,但是不影響現在要操作的條件。*/
                            
                            boolean created = false;
                            try {  
                                Cell[] rs;
                                int m, j;
                                // 再次判斷沒有這個cell, 前面if判斷了是空,走到這里時候有可能別人放進去了並且cellsBusy從0變到1再變到0了。如果不是null了,就不放,下次再來(直接更新)。
                                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;// cell不存在,但是有人修改cells,collide = false,
                }
                //1.2有這個線程的cell
                //wasUncontended=false重新來
                else if (!wasUncontended) // wasUncontended=false表示更新失敗了,再來,wasUncontended=true下次不進這里直接去cas更新,否則先不cas先再來一次。
                    wasUncontended = true; 
                //  1.3有這個線程的cell
                //wasUncontended=true,更新失敗了重新來。
                else if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x))))//這個線程有Cell,去更新。  
                    break;// 更新成功,退出。
                // 1.4有這個線程的cell
                //wasUncontended=true,更新失敗,cells初始化擴容了,重新來
                else if (n >= NCPU || cells != as) // CPU能夠並行的CAS操作的最大數量是它的核心數 ,cells被改變了(擴容了肯定重新來)。
                    collide = false;  
                //  1.5有這個線程的cell
                //wasUncontended=true,更新失敗,cells沒有初始化擴容,collide=false,重新來
                else if (!collide) //=false走這里,collide=true,下次不走這里直接去擴容否則先不去擴容先再來一次。
                    collide = true; 
                // 1.6有這個線程的cell
                //wasUncontended=true,更新失敗,cells沒有初始化擴容,collide=true,占用cells,擴容完成,重新來
                //有這個線程的cell,cas失敗,說明2個線程同時更新這個cell,就擴容。既然你不讓我加,競爭這么厲害,那么擴容試試看。
                else if (cellsBusy == 0 && /* 這期間其他線程可以做很多事 */casCellsBusy()) { 
                    
                    //不可能多個線程同時進這里面來。
                    
                    try {
                        if (cells == as) { //鎖住cells了,最開始as = cells,但是現在as不一定=cells,所以判斷cells沒變擴容,
                            Cell[] rs = new Cell[n << 1];// 執行2倍擴容
                            for (int i = 0; i < n; ++i)
                                rs[i] = as[i];
                            cells = rs;
                        }
                    } finally {
                        cellsBusy = 0;// 釋放鎖
                    }
                    collide = false;// 擴容意向為false
                    continue; // 擴容后還沒有設置值(肯定重新來)
                }
                //1.7 有這個線程的cell
                h = advanceProbe(h);// 修改當前線程的hash,降低hash沖突(線程hash改變是無所謂的,關注的是里面的值,與哪個線程放進去無關),避免下次還映射到這個cell。
            } 
            
            // 2。某個線程執行時候,前一個if判斷:沒有分段更新,cells==null或者cells.length=0。走到這里時候cells有可能不為空了,但是要進入這if必須:cellsBusy=0, //同時cells還是剛才那個as=null(沒有擴容和初始化)並且casCellsBusy()搶成功,就去初始化。 //線程執行到這里之前判斷:【沒有cells】,並且現在【沒人擴容或者初始化,並且cells為空】就初始化。
            else if (cellsBusy == 0 && cells == as && /*這期間其他線程可以做很多事*/casCellsBusy()) {// cellsBusy=1,別的線程就不能動cells
                
                //不可能多個線程同時進這里面來。
                
                boolean init = false;
                try {  
                    if (cells == as) { //鎖住cells了,但是cells不一定=as=空或者null了, 鎖住之后一定要再檢測一次,如果還是null就初始化
                        Cell[] rs = new Cell[2];// 初始化時只創建兩個單元
                        rs[h & 1] = new Cell(x);// 對其中一個單元進行累積操作,另一個不管,繼續為null
                        cells = rs;
                        init = true;
                    }
                } finally {
                    cellsBusy = 0;//  釋放鎖
                }
                if (init)//  初始化成功退出,初始化失敗繼續來
                    break;
            }
            
            // 3。走到這里:前面判斷不成立(不代表現在的前面判斷也不成立)之前判斷:cells=null或者cells.length=0【沒有cells】並且cellsBusy=1或者 cells被改變了【有人正在初始化或擴容】或者casCellsBusy()失敗。
            //有了分段更新,還是可以用base,提高效率。准備去擴容的,但是現在有可能別人已經擴容了(cells != as)或者casCellsBusy()失敗(搶着去擴容沒有搶成功)
            else if (casBase(v = base, ((fn == null) ? v + x : fn.applyAsLong(v, x)))) 
                break; // 更新base,成功就退出。
        }
        
        /*如果Cells表為空,嘗試獲取鎖之后初始化表(初始大小為2);
        如果Cells表非空,對應的Cell為空,自旋鎖未被占用,嘗試獲取鎖,添加新的Cell;
        如果Cells表非空,找到線程對應的Cell,嘗試通過CAS更新該值;
        如果Cells表非空,線程對應的Cell CAS更新失敗,說明存在競爭,嘗試獲取自旋鎖之后擴容,將cells數組擴大,降低每個cell的並發量后再試*/
    }

    // double更long的邏輯基本上是一樣的
    final void doubleAccumulate(double x, DoubleBinaryOperator fn, boolean wasUncontended) {
        int h;
        if ((h = getProbe()) == 0) {
            ThreadLocalRandom.current(); // force initialization
            h = getProbe();
            wasUncontended = true;
        }
        boolean collide = false; // True if last slot nonempty
        for (;;) {
            Cell[] as;
            Cell a;
            int n;
            long v;
            if ((as = cells) != null && (n = as.length) > 0) {
                if ((a = as[(n - 1) & h]) == null) {
                    if (cellsBusy == 0) { // Try to attach new Cell
                        Cell r = new Cell(Double.doubleToRawLongBits(x));
                        if (cellsBusy == 0 && casCellsBusy()) {
                            boolean created = false;
                            try { // Recheck under lock
                                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; // Slot is now non-empty
                        }
                    }
                    collide = false;
                } else if (!wasUncontended) // CAS already known to fail
                    wasUncontended = true; // Continue after rehash
                else if (a.cas(v = a.value, ((fn == null) ? Double.doubleToRawLongBits(Double.longBitsToDouble(v) + x)
                        : Double.doubleToRawLongBits(fn.applyAsDouble(Double.longBitsToDouble(v), x)))))
                    break;
                else if (n >= NCPU || cells != as)
                    collide = false; // At max size or stale
                else if (!collide)
                    collide = true;
                else if (cellsBusy == 0 && casCellsBusy()) {
                    try {
                        if (cells == as) { // Expand table unless stale
                            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; // Retry with expanded table
                }
                h = advanceProbe(h);
            } else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
                boolean init = false;
                try { // Initialize table
                    if (cells == as) {
                        Cell[] rs = new Cell[2];
                        rs[h & 1] = new Cell(Double.doubleToRawLongBits(x));
                        cells = rs;
                        init = true;
                    }
                } finally {
                    cellsBusy = 0;
                }
                if (init)
                    break;
            } else if (casBase(v = base, ((fn == null) ? Double.doubleToRawLongBits(Double.longBitsToDouble(v) + x)
                    : Double.doubleToRawLongBits(fn.applyAsDouble(Double.longBitsToDouble(v), x)))))
                break; // Fall back on using base
        }
    }

    private static final sun.misc.Unsafe UNSAFE;
    private static final long BASE;
    private static final long CELLSBUSY;
    private static final long PROBE;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> sk = Striped641.class;
            BASE = UNSAFE.objectFieldOffset(sk.getDeclaredField("base"));
            CELLSBUSY = UNSAFE.objectFieldOffset(sk.getDeclaredField("cellsBusy"));
            Class<?> tk = Thread.class;
            PROBE = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomProbe"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
    
    // 一個Cell里面一個value,可以看成是一個簡化的AtomicLong,通過cas操作來更新value的值
    // @sun.misc.Contended是一個高端的注解,代表使用緩存行填來避免偽共享
    @sun.misc.Contended
    static final class Cell {
        volatile long value;// cas更新其值
        Cell(long x) {
            value = x;
        }
        final boolean cas(long cmp, long val) {// cas更新
            return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
        }
        private static final sun.misc.Unsafe UNSAFE;
        private static final long valueOffset;
        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class<?> ak = Cell.class;
                valueOffset = UNSAFE.objectFieldOffset(ak.getDeclaredField("value"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    }

}
public class LongAdder1 extends Striped641 implements Serializable {
    private static final long serialVersionUID = 7249069246863182397L;

    public LongAdder1() {
    }
    
    /*看到這里我想應該有很多人明白為什么LongAdder會比AtomicLong更高效了,
    沒錯,唯一會制約AtomicLong高效的原因是高並發,高並發意味着CAS的失敗幾率更高,
    重試次數更多,越多線程重試,CAS失敗幾率又越高,變成惡性循環,AtomicLong效率降低。 
    那怎么解決?** LongAdder給了我們一個非常容易想到的解決方案:減少並發,將單一value的更新壓力分擔到多個value中去
    (每個線程更新value數組里面的自己value【多個線程訪問的同一個數組(也只有一個數組)但是cas更新的是只是其中一個value】 【因為數組有限,所以不同的線程也會出現同時cas更新一個value的情況】【會出現:多個線程同時訪問這個數組的同一個cell】, 不再是多個線程更新同一個value導致cas經常失敗), 降低單個value的 “熱度”,分段更新 */
    /*這樣,線程數再多也會分擔到多個value上去更新,只需要增加value就可以降低 value的 “熱度”  
    AtomicLong中的 惡性循環不就解決了嗎? cells 就是這個 “段” cell中的value 就是存放更新值的, 
    這樣,當我需要總數時,把cells 中的value都累加一下不就可以了么!!*/
    /*當然,聰明之處遠遠不僅僅這里,在看看add方法中的代碼,casBase方法可不可以不要,直接分段更新,上來就計算 索引位置,然后更新value?
    答案是不好,不是不行,因為,casBase操作等價於AtomicLong中的CAS操作,要知道,LongAdder這樣的處理方式是有壞處的,
    分段操作必然帶來空間上的浪費,可以空間換時間,但是,能不換就不換,看空間時間都節約~! 
    所以,casBase操作保證了在低並發時,不會立即進入分支做分段更新操作,因為低並發時,
    casBase操作基本都會成功,只有並發高到一定程度了,才會進入分支,
    所以,Doug Lea對該類的說明是:** 低並發時LongAdder和AtomicLong性能差不多,高並發時LongAdder更高效!***/
    /*因為低並發時候,使用的是base的原子更新,沒有啟用分段更新(cells=null,並且casBase成功),高並發才啟用分段更新。*/
    /*如此,longAccumulate中做了什么事,也基本略知一二了,因為cell中的value都更新失敗(說明該索引到這個cell的線程也很多
    ,並發也很高時) 或者cells數組為空時才會調用longAccumulate,*/
    
    // +x,並發計數器LongAdder加X。要么在base+x更新要么在Cell[]數組里面找到對應的Cell+x更新。
    public void add(long x) {//base和cells只有一個,並且是LongAdder的屬性。
        Cell[] as;
        long b, v;
        int m;
        Cell a;
        //cells!=null不用判斷后面進去(表明已經啟用了分段更新),cells=null並且base的cas更新失敗進去(表示沒有啟用分段更新但是高並發了, //需要啟用分段更新),cells=null並且base的cas更新成功就退出(沒有啟用分段更新,並且不是高並發,此時跟AotomicLong是一樣的)。
        //並發時候更新失敗,AtomicLong的處理方式是死循環嘗試更新,直到成功才返回,而LongAdder則是進入這個分支。
        if ((as = cells) != null || !casBase(b = base, b + x)/*cas把base的值從b變成b+x*/) {
            //進來:1.已經啟用分段更新了。2.沒有啟用分段更新但是cas失敗了表示高並發了。否則:沒有啟用分段更新並且不是高並發,就不進來。
            boolean uncontended = true;
            if (as == null //cells=null進去,沒有啟用分段更新(進來了)表示高並發了。
                || (m = as.length - 1) < 0  //cells.length<=0,沒有啟用分段更新(進來了)表示高並發了。
                || (a = as[getProbe() & m]) == null  //對as的長度取余,從as中獲取這個線程對應的a Cell。=null表示還沒有這個線程對應的cell,
                || !(uncontended = a.cas(v = a.value, v + x)))  //a這個Cell里面的value增加x失敗, 更新成功就不會進下面了。
                
                //1.cells=null。2.cells!=null但沒有這個線程的Cell。2.有這個線程的Cell但是更新失敗了。
                longAccumulate(x, null, uncontended); //uncontended=false表示更新失敗了,=true表示沒有這個線程的Cell(不可能是更新成功了,更新成功就進不來這里)。
        }
    }

    public void increment() {
        add(1L);
    }

    public void decrement() {
        add(-1L);
    }

    //將多個cell數組中的值加起來的和就類似於AtomicLong中的value
    // 此返回值可能不是絕對准確的,因為調用這個方法時還有其他線程可能正在進行計數累加,
    //     方法的返回時刻和調用時刻不是同一個點,在有並發的情況下,這個值只是近似准確的計數值 // 高並發時,除非全局加鎖,否則得不到程序運行中某個時刻絕對准確的值,但是全局加鎖在高並發情況下是下下策
    // 在很多的並發場景中,計數操作並不是核心,這種情況下允許計數器的值出現一點偏差,此時可以使用LongAdder
    // 在必須依賴准確計數值的場景中,應該自己處理而不是使用通用的類。
    public long sum() {
        Cell[] as = cells;
        Cell a;
        long sum = base;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    sum += a.value;
            }
        }
        return sum;
    }

    public void reset() {
        Cell[] as = cells;
        Cell a;
        base = 0L;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    a.value = 0L;
            }
        }
    }

    public long sumThenReset() {
        Cell[] as = cells;
        Cell a;
        long sum = base;
        base = 0L;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null) {
                    sum += a.value;
                    a.value = 0L;
                }
            }
        }
        return sum;
    }

    public String toString() {
        return Long.toString(sum());
    }

    public long longValue() {
        return sum();
    }

    public int intValue() {
        return (int) sum();
    }

    public float floatValue() {
        return (float) sum();
    }

    public double doubleValue() {
        return (double) sum();
    }

    private static class SerializationProxy implements Serializable {
        private static final long serialVersionUID = 7249069246863182397L;

        private final long value;//LongAdder1的總和

        SerializationProxy(LongAdder1 a) {
            value = a.sum();
        }

        private Object readResolve() {
            LongAdder1 a = new LongAdder1();
            a.base = value;
            return a;
        }
    }

    private Object writeReplace() {
        return new SerializationProxy(this);
    }

    private void readObject(java.io.ObjectInputStream s) throws java.io.InvalidObjectException {
        throw new java.io.InvalidObjectException("Proxy required");
    }

}
public abstract class Number1 implements java.io.Serializable {
    
    public abstract int intValue();

    public abstract long longValue();

    public abstract float floatValue();

    public abstract double doubleValue();

    /*
     System.out.println((byte)127);//127
    System.out.println((byte)128);//-128
    System.out.println((byte)129);//-127
    System.out.println((byte)255);//-1
    System.out.println((byte)256);//0
    System.out.println((byte)257);//1
     */
    public byte byteValue() {
        return (byte) intValue();
    }

    public short shortValue() {
        return (short) intValue();
    }

    private static final long serialVersionUID = -8742448824652078965L;
}

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM