Java中的LongAdder类简单的说明

要说这篇博客只能算个简单说明吧,我个人对于看源码其实并不是很有心得,只是因为上了年纪,二十又四,在此记录一下,怕是过后忘记罢了。

LongAdder,在研究并发时看到了这个类,jdk8的新类,我对于jdk8的新特性还没完全体验完,jdk10都出来了,好像是可以写var这种弱类型引用了吧,变化真是太快了,不多说废话了,来说今天的主角。

它号称比Atomic更高效,那么就一起看看源码吧,建议打开ide,然后搜索这个类的源码来一起看,ide有注释,有括号的匹配等,看起来要方便的多。比看我这大段代码强很多。在看源码之前,对这种包含设计思想的类呢,个人喜欢查找一下它的一个设计思路(不知道这么说对不对,就是如何高效实现的过程吧),下面这张图是我在简书网站上截取的图片:

Java中的LongAdder类简单的说明_第1张图片

我觉得不难理解,脑子里有了这个概念之后再来看源码就相对好一点了,毕竟我了解了实现思路,脑子里会有一个印象,对出现的操作,变量的含义就不是那么难以理解了。贴一下源码:

Java中的LongAdder类简单的说明_第2张图片

上面截取这一部分,可以看到,有一个不带参数的构造器,本身继承自Striped64 这个类
挑截图上的add方法来讲吧,大家打开源码也可以看到,其他方法都是会调用它。

首先是一下变量的初始化,Cell[] as cell是啥??点进去看。

Java中的LongAdder类简单的说明_第3张图片

很简短的一个class,它是Striped64的一个内部类,里面有一个value属性。并且有个cas方法,里面就是调用了unsafe的方法,与Atomic操作一样。

回到add方法,进入if判断,看一下这几个判断条件,拿第一来说,cells是否==null,找一下cells,点击可以发现是在Striped64中,没有初始化它的操作,只有在函数中的,说明开始就是null的;casbase函数,点进去看看还是与Atomic一样的cas方法,不过前面的条件满足了,后面的就不会执行了。也就是说第一次执行,if条件的第一条满足了,进入if。

发现进入后还有一个if,第一条==null又是满足了,其他的几个条件:m=as.length-1>0,数组不为空,但是length是<1的,其实这跟空差不多了;(a = as[getProbe() & m]) == null 这个是说cell数组随机取出的一个cell元素是空的,也就是没有初始化过;!(uncontended = a.cas(v = a.value, v + x))这个看上去很明显,做了一次cas操作失败了。

上述条件满足任何一个,都会进入到if,执行longAccumulate(x, null, uncontended);
迫不及待的点进去,这个长代码我不贴了,大家自己的ide看的很清楚,贴上去这么长的代码还有这么多括号,相信自己对括号也是很难,所以不要懒了,动动手指打开一下ide操作一下。

看到有三条路径,那么贴出第一条的代码

Java中的LongAdder类简单的说明_第4张图片

这个红线指出的条件之前就说过,这里再重复一遍:cells !=null并且不止是初始化过,还有长度,并且,随机选取的cells数组中的某一个元素是空的,是空的,就是说明没有被用过。

cellsBusy,有个这个变量,从字面翻译过来是cells正在忙,应该是一个操作执行的 ‘加锁’ 变量,==0,可以猜测出,0值代表的是不忙,那1就代表忙了呗,大概是这样吧,这里是猜测,继续看,不忙的话,并且casCellBusy()返回true,就创建了一个cell,并且再次判断了cellsbusy,官方给了个注释:optimistcally create 翻译过来是乐观地创建,应该是乐观锁的一种写法。try-catch中的操作简单来说就是将创建好的cell添加到了cells的数组中。finally,将这个乐观锁标志释放。
上面提到的cascellbusy()这里说一下,里面其实很简单,调用了unsafe的方法,去改变这个标记量,可以理解为加一个锁。

接下来是第二个if,这个简单多了,满足了条件就创建一个cells数组,它就是在这里初始化的,初始默认数组大小是2。

Java中的LongAdder类简单的说明_第5张图片

最后一个if代码很少,简单说一下,就是上述条件不满足的话,再进行一次cas更新。

上述在讲解第一个if时,漏掉了一部分,红色箭头的if,在代码里可以看到有后续的else if ,这里补充。

Java中的LongAdder类简单的说明_第6张图片

Java中的LongAdder类简单的说明_第7张图片

  1. wasUncontended这个分支表示,之前在add函数中所做的cas操作失败了,可以看到英文注释,重新重置这个变量,重新循环。
  2. 第二个代表再试一次cas操作。
  3. (n >= NCPU || cells != as) Ncpu表示运行时能够获得到的cpu core数量,大于了的话,说明就要排队了,collide是冲突的意思,冲突了,那么上面的if就不能获得锁,知道n>=cpu这个条件不满足了,会进入到下面的if中,将标记位解除。
  4. 解除资源冲突标记位
  5. 最后这个,看注释:扩容cells大小。

整个流程其实还算简单:大致总结一下,通过cells这个数组,每个字段初始化都是0的,然后提供了add,increasement等方法,变化的数量都是1,这就保证了cells数组的和就是多个线程操作的总和,操作到相同cell,会有cas操作来进行限制,因此在高并发的时候,将原来一个变量,扩充到了多个。效率提高了。

注意:获取值是要配置sum方法使用的。此方法很简单,也没有所得概念在其中,是将cells中的所有元素相加得到总和。

PS:随笔博客,忽略了一下东西,只是挑了其中一个方法大概讲解了一下这个类,还请大家留言批评指正

你可能感兴趣的:(java方面,java,web的各种杂谈)