AtomicLong是作用是对长整形进行原子操作,显而易见,在java1.8中新加入了一个新的原子类LongAdder,该类也可以保证Long类型操作的原子性,相对于AtomicLong,LongAdder有着更高的性能和更好的表现,可以完全替代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;
}

LongAdder源码分析的更多相关文章

  1. 死磕 java并发包之LongAdder源码分析

    问题 (1)java8中为什么要新增LongAdder? (2)LongAdder的实现方式? (3)LongAdder与AtomicLong的对比? 简介 LongAdder是java8中新增的原子 ...

  2. LongAdder 源码分析

    LongAdder LongAdder 能解决什么问题?什么时候使用 LongAdder? 1)LongAdder 内部包含一个基础值[base]和一个单元[Cell]数组. 没有竞争的情况下,要累加 ...

  3. 死磕 java集合之ConcurrentHashMap源码分析(三)

    本章接着上两章,链接直达: 死磕 java集合之ConcurrentHashMap源码分析(一) 死磕 java集合之ConcurrentHashMap源码分析(二) 删除元素 删除元素跟添加元素一样 ...

  4. JDK源码分析(12)之 ConcurrentHashMap 详解

    本文将主要讲述 JDK1.8 版本 的 ConcurrentHashMap,其内部结构和很多的哈希优化算法,都是和 JDK1.8 版本的 HashMap是一样的,所以在阅读本文之前,一定要先了解 Ha ...

  5. ConcurrentHashMap JDK 1.6 源码分析

    前言 前段时间把 JDK 1.6 中的 HashMap 主要的一些操作源码分析了一次.既然把 HashMap 源码分析了, 就顺便把 JDK 1.6 中 ConcurrentHashMap 的主要一些 ...

  6. 并发-ConcurrentHashMap源码分析

    ConcurrentHashMap 参考: http://www.cnblogs.com/chengxiao/p/6842045.html https://my.oschina.net/hosee/b ...

  7. 2. Sentinel源码分析—Sentinel是如何进行流量统计的?

    这一篇我还是继续上一篇没有讲完的内容,先上一个例子: private static final int threadCount = 100; public static void main(Strin ...

  8. 源码分析 Alibaba sentinel 滑动窗口实现原理(文末附原理图)

    要实现限流.熔断等功能,首先要解决的问题是如何实时采集服务(资源)调用信息.例如将某一个接口设置的限流阔值 1W/tps,那首先如何判断当前的 TPS 是多少?Alibaba Sentinel 采用滑 ...

  9. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

随机推荐

  1. Centos7 python虚拟环境virtualenv和virtualenvwrapper简单介绍

    我的系统版本是 [root@localhost ~]# cat /etc/os-release 我的Python版本是 [root@localhost ~]# python3 -V 关于如何安装Pyt ...

  2. Java自学-I/O 字节流

    Java 字节流 InputStream OutputStream InputStream字节输入流 OutputStream字节输出流 用于以字节的形式读取和写入数据 步骤 1 : ASCII码 所 ...

  3. PHP上传图片基本代码示例

    一.HTML代码如下: <form name="form2" method="post" action="?type=add" enc ...

  4. 中国工业的下一个十年在哪里?APS系统或将引领智能化转型

    为什么众多的ERP软件公司没有推出相关产品,当然可以肯定的是并非客户没有此观念,如果一定要说,也只能说目前的需求还不是非常强烈,从ERP厂商非常急切的与APS公司合作,甚至有高价购买APS公司代码的情 ...

  5. 2.redis 和 memcached 有什么区别?redis 的线程模型是什么?为什么 redis 单线程却能支撑高并发?

    作者:中华石杉 面试题 redis 和 memcached 有什么区别?redis 的线程模型是什么?为什么 redis 单线程却能支撑高并发? 面试官心理分析 这个是问 redis 的时候,最基本的 ...

  6. 影响Python行为的环境变量

    目录 影响Python行为的环境变量 环境变量 1. PYTHONHOME 2. PYTHONPATH 3. PYTHONSTARTUP 4. PYTHONOPTIMIZE 5. PYTHONBREA ...

  7. Linux的web服务的介绍

    web(World Wide Web)即全球广域网,也称为万维网,它是一种基于超文本和HTTP的.全球性的.动态交互的.跨平台的分布式图形信息系统.是建立在Internet上的一种网络服务,为浏览者在 ...

  8. xadmin引入celery4.0执行异步任务与定时任务

    一.安装 pip install celery pip install django-celery-beat pip install django-celery-results pip install ...

  9. InterpretML 微软可解释性机器学习包

    InterpretML InterpretML: A Unified Framework for Machine Learning Interpretability https://github.co ...

  10. djabgo 中间件

    1.中间件是发生在request和response 之间,都会经过中间键, 上述截图中的中间件都是django中的,我们也可以自己定义一个中间件,我们可以自己写一个类,但是必须继承Middleware ...