对于longAdder的应用,也是在看到concurrentHashMap源码中,统计数组中元素个数的时候,看到了Doug Lea写的一个注释,才开始去关注这个类,这里主要记录下自己对该类源码的学习笔记
longAdder核心思想
通常,在说到这个类的时候,会和atomic中原子类有关系,atomicInteger底层的话,通过unsafe中的native方法进行compareAndSwap方法去进行值的更新,同时,在atomic类中声明的value是volatile修饰,保证了可见性;这个是没问题的,但是有一个问题:如果有多个线程来对一个值进行修改的时候,同一时刻,只会有一个线程去更新成功,其他的线程会不停的cas自旋,这样的话,会消耗CPU的资源;此时:LongAdder就可以出场了,简单而言,longAdder是以空间换时间
对于atomicLong来说,只有前一个value,如果多个线程对一个字段进行加1,其他线程会不停得自旋,直到当前线程对该值+1成功;
那LongAdder怎么做的呢?在多个线程对同一个变量进行+1的时候,会把对value的压力,分散到一个cell数组中,也就是图中后面的value1、value2…valueN,这样的话,虽然需要额外再维护一个数组,但是可以提高并发量,也就是所谓的以空间换时间
源码
这是longAdder的继承关系
我们来看对应的add方法
add()
/**
* Adds the given value.
*
* cells是volatile修饰的cell数组
* base:也是volatile修饰的变量,在尝试+1 的时候,其实就是对base变量进行+1
* 如果base变量+1失败,就需要将+1的操作,转移到cell数组中的某个节点对应的value
* 在获取总数的时候,就是 base + cell[0] + cell[1] + ... + cell[n]
*
* @param x the value to add
*/
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
/**
* 如果cas设置base的值成功,那就无需进入,直接结束运行
* 如果cas设置失败,有可能是并发请求导致失败,就会将该线程需要 + 1的动作,放到cell中执行
*/
if ((as = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true;
/**
* 下面这个if判断,最为重要的是最后一个cas
* 如果代码执行到最后一个cas表示,当前cells[i]位置不为null,直接cas设置cells[i]位置的值 +1
* 如果成功,结束运行
* 否则就进入longAccumulate
*
* getProbe()是根据当前线程生成的一个值,其实是为了判断当前线程 + 1的动作,需要放在数组中的哪个位置执行
*/
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
longAccumulate