Java并發(fā)編程之LongAdder源碼解析
前言
上一篇文章 Java并發(fā)編程之原子類(二)中介紹了LongAdder
常用的方法,今天我們根據(jù)源碼來(lái)分析一下它的基本實(shí)現(xiàn)流程。
This class is usually preferable to AtomicLong when multiple threads update a common sum that is used for purposes such as collecting statistics, not for fine-grained synchronization control. Under low update contention, the two classes have similar characteristics. But under high contention, expected throughput of this class is significantly higher, at the expense of higher space consumption.
上面這段話是LongAdder
源碼注釋中的一部分,翻譯過(guò)來(lái)意思大概是
當(dāng)多個(gè)線程更新用于收集統(tǒng)計(jì)信息但不用于細(xì)粒度同步控制的目的的公共和時(shí),此類通常優(yōu)于AtomicLong
。 在低更新爭(zhēng)用下,這兩個(gè)類具有相似的特征。 但在高爭(zhēng)用的情況下,這一類的預(yù)期吞吐量明顯更高,但代價(jià)是空間消耗更高。
也就是說(shuō)LongAdder
在并發(fā)度高的情況下效率更高,但是代價(jià)是以空間換時(shí)間。
通俗地解釋一下LongAdder
的原理:當(dāng)并發(fā)少的時(shí)候,累加操作只在一個(gè)變量base
上執(zhí)行就夠用了,所以和AtomicLong
類似;但是當(dāng)并發(fā)量上來(lái)的時(shí)候,如果還是在變量base
上進(jìn)行操作就會(huì)有很多線程阻塞,所以就創(chuàng)建一個(gè)數(shù)組cells
,在數(shù)組的每一個(gè)元素上都可以進(jìn)行累加,最后計(jì)算結(jié)果時(shí)再就算一下base
和cells
數(shù)組每個(gè)元素的和就行了。而線程具體在數(shù)組的哪一位進(jìn)行操作可以通過(guò)計(jì)算hash
來(lái)確定索引位置。
源碼簡(jiǎn)介
LongAdder
從父類Striped64
繼承過(guò)來(lái)的屬性,這里的Cell
是一個(gè)用來(lái)進(jìn)行累加操作的內(nèi)部類,內(nèi)部有一個(gè)value
屬性來(lái)存儲(chǔ)累加的值。
// CPU核心數(shù) static final int NCPU = Runtime.getRuntime().availableProcessors(); // 并發(fā)高時(shí)進(jìn)行累加的Cell數(shù)組 transient volatile Cell[] cells; // 多個(gè)線程沒(méi)有競(jìng)爭(zhēng)時(shí)在base上進(jìn)行累加 transient volatile long base; // Cell數(shù)組是否正在創(chuàng)建或擴(kuò)容 transient volatile int cellsBusy;
累加操作方法increment()
實(shí)際調(diào)用的是add(1L)
,所以我們直接來(lái)看add
方法
public void add(long x) { Cell[] as; long b, v; int m; Cell a; if ((as = cells) != null || !casBase(b = base, b + x)) { boolean uncontended = true; // 表示沒(méi)有競(jìng)爭(zhēng) if (as == null || (m = as.length - 1) < 0 || (a = as[getProbe() & m]) == null || !(uncontended = a.cas(v = a.value, v + x))) longAccumulate(x, null, uncontended); } }
首先來(lái)看第一個(gè)if
語(yǔ)句,初始狀況下cells
是null
,所以會(huì)進(jìn)行casBase
操作,也就是在base
變量上進(jìn)行累加,如果操作成功了說(shuō)明當(dāng)前沒(méi)有競(jìng)爭(zhēng),所以就結(jié)束了。
當(dāng)并發(fā)量上來(lái)的時(shí)候,進(jìn)行casBase
方法就有可能會(huì)失敗,所以這時(shí)進(jìn)入第二個(gè)if
語(yǔ)句判斷。
- 第一次進(jìn)來(lái)時(shí)
Cell
數(shù)組as
是null
,所以就會(huì)執(zhí)行longAccumulate
,對(duì)Cell
數(shù)組as
進(jìn)行初始化并且在索引1
位置累加1
; - 之后再執(zhí)行到這個(gè)
if
語(yǔ)句as
就不是null
了,而且數(shù)組長(zhǎng)度也大于0
a = as[getProbe() & m]) == null
,這句話簡(jiǎn)單的理解就是在數(shù)組as
中隨機(jī)找到一個(gè)索引位置,判斷該位置的值是不是null
,如果是null
的話就執(zhí)行longAccumulate
,不是null
繼續(xù)向下判斷!(uncontended = a.cas(v = a.value, v + x))
這句話的意思是,在找到的這個(gè)索引位置進(jìn)行累加操作,如果成功了就結(jié)束操作,如果失敗了就執(zhí)行longAccumulate
add
方法的流程到這里基本就講完了,是不是還挺好理解的。注意到這里有三種情況會(huì)進(jìn)入到longAccumulate
方法,下篇文章再來(lái)講解具體做了哪些事。
以上就是Java并發(fā)編程之LongAdder源碼解析的詳細(xì)內(nèi)容,更多關(guān)于Java并發(fā)LongAdder的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring配置多數(shù)據(jù)源導(dǎo)致事物無(wú)法回滾問(wèn)題
這篇文章主要介紹了Spring配置多數(shù)據(jù)源導(dǎo)致事物無(wú)法回滾問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01js判斷是否是移動(dòng)設(shè)備登陸網(wǎng)頁(yè)的簡(jiǎn)單方法
這篇文章主要介紹了js判斷是否是移動(dòng)設(shè)備登陸網(wǎng)頁(yè)的簡(jiǎn)單方法,需要的朋友可以參考下2014-02-02