<div class="article-content">
<h2 class="heading">问题</h2>
<p>(1)java8中为什么要新增LongAdder?</p>
<p>(2)LongAdder的实现方式?</p>
<p>(3)LongAdder与AtomicLong的对比?</p>
<h2 class="heading">简介</h2>
<p>LongAdder是java8中新增的原子类,在多线程环境中,它比AtomicLong性能要高出不少,特别是写多的场景。</p>
<p>它是怎么实现的呢?让我们一起来学习吧。</p>
<h2 class="heading">原理</h2>
<p>LongAdder的原理是,在最初无竞争时,只更新base的值,当有多线程竞争时通过分段的思想,让不同的线程更新不同的段,最后把这些段相加就得到了完整的LongAdder存储的值。</p>
<p></p>
<figure>
<figcaption></figcaption>
</figure>
<p></p>
<h2 class="heading">源码分析</h2>
<p>LongAdder继承自Striped64抽象类,Striped64中定义了Cell内部类和各重要属性。</p>
<h3 class="heading">主要内部类</h3>
<pre class="blockcode"><code class="hljs java copyable"><span class="hljs-comment">// Striped64中的内部类,使用@sun.misc.Contended注解,说明里面的值消除伪共享</span>
<span class="hljs-meta">@sun</span>.misc.Contended <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Cell</span> </span>{
<span class="hljs-comment">// 存储元素的值,使用volatile修饰保证可见性</span>
<span class="hljs-keyword">volatile</span> <span class="hljs-keyword">long</span> value;
Cell(<span class="hljs-keyword">long</span> x) { value = x; }
<span class="hljs-comment">// CAS更新value的值</span>
<span class="hljs-function"><span class="hljs-keyword">final</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">cas</span><span class="hljs-params">(<span class="hljs-keyword">long</span> cmp, <span class="hljs-keyword">long</span> val)</span> </span>{
<span class="hljs-keyword">return</span> UNSAFE.compareAndSwapLong(<span class="hljs-keyword">this</span>, valueOffset, cmp, val);
}
<span class="hljs-comment">// Unsafe实例</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> sun.misc.Unsafe UNSAFE;
<span class="hljs-comment">// value字段的偏移量</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> valueOffset;
<span class="hljs-keyword">static</span> {
<span class="hljs-keyword">try</span> {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> ak = Cell.class;
valueOffset = UNSAFE.objectFieldOffset
(ak.getDeclaredField(<span class="hljs-string">"value"</span>));
} <span class="hljs-keyword">catch</span> (Exception e) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Error(e);
}
}
}
<span class="copy-code-btn">复制代码</span></code></pre>
<p>Cell类使用@sun.misc.Contended注解,说明是要避免伪共享的。</p>
<p>使用Unsafe的CAS更新value的值,其中value的值使用volatile修饰,保证可见性。</p>
<p>关于Unsafe的介绍请查看【<a href="https://link.juejin.im?target=https%3A%2F%2Fmp.weixin.qq.com%2Fs%2F0s-u-MysppIaIHVrshp9fA">死磕 java魔法类之Unsafe解析</a>】。</p>
<p>关于伪共享的介绍请查看【<a href="https://link.juejin.im?target=https%3A%2F%2Fmp.weixin.qq.com%2Fs%2Frd13SOSxhLA6TT13N9ni8Q">杂谈 什么是伪共享(false sharing)?</a>】。</p>
<h3 class="heading">主要属性</h3>
<pre class="blockcode"><code class="hljs java copyable"><span class="hljs-comment">// 这三个属性都在Striped64中</span>
<span class="hljs-comment">// cells数组,存储各个段的值</span>
<span class="hljs-keyword">transient</span> <span class="hljs-keyword">volatile</span> Cell[] cells;
<span class="hljs-comment">// 最初无竞争时使用的,也算一个特殊的段</span>
<span class="hljs-keyword">transient</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">long</span> base;
<span class="hljs-comment">// 标记当前是否有线程在创建或扩容cells,或者在创建Cell</span>
<span class="hljs-comment">// 通过CAS更新该值,相当于是一个锁</span>
<span class="hljs-keyword">transient</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">int</span> cellsBusy;
<span class="copy-code-btn">复制代码</span></code></pre>
<p>最初无竞争或有其它线程在创建cells数组时使用base更新值,有过竞争时使用cells更新值。</p>
<p>最初无竞争是指一开始没有线程之间的竞争,但也有可能是多线程在操作,只是这些线程没有同时去更新base的值。</p>
<p>有过竞争是指只要出现过竞争不管后面有没有竞争都使用cells更新值,规则是不同的线程hash到不同的cell上去更新,减少竞争。</p>
<h3 |
|