摘要:最終依舊使用來更新值。此時(shí)使用能更好地提升性能。適用于高并發(fā)情況下的計(jì)數(shù)操作,利用與相似的原理,以空間換時(shí)間,提高了實(shí)際的計(jì)數(shù)效率。
AtomicLong
/** * Atomically increments by one the current value. * * @return the updated value */ public final long incrementAndGet() { return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L; }unsafe
public final long getAndAddLong(Object var1, long var2, long var4) { long var6; do { var6 = this.getLongVolatile(var1, var2); } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4)); // 關(guān)注重點(diǎn):if(var2 == var6) return var6; }
var1 調(diào)用原方法 incrementAndGet 即自身的對象
var2 原對象當(dāng)前(工作內(nèi)存中的)值
var4 要加上去的值
var6 調(diào)用底層方法 getLongVolatile 獲得當(dāng)前(主內(nèi)存中的)值,如果沒其他線程修改即與 var2 相等
compareAndSwapLong
var2 與 var6 為什么可能會不一樣?
在并發(fā)環(huán)境下,工作內(nèi)存中的值 var2 與主內(nèi)存中的值 var6 之間的可能不一樣(JMM)
// 獲取主內(nèi)存里的值 public native long getLongVolatile(Object var1, long var2); // CAS 操作 public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
native關(guān)鍵字說明其修飾的方法是一個(gè)原生態(tài)方法,方法對應(yīng)的實(shí)現(xiàn)不是在當(dāng)前文件,而是在用其他語言(如 C 和 C++)實(shí)現(xiàn)的文件中。Java 語言本身不能對操作系統(tǒng)底層進(jìn)行訪問和操作,但是可以通過JNI接口調(diào)用其他語言來實(shí)現(xiàn)對底層的訪問。LongAdder
public void increment() { add(1L); } 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; 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); // <- 重點(diǎn) } }Cell
Cell 類,是一個(gè)普通的二元算術(shù)累積單元,它在 Striped64 里面。Striped64 這個(gè)類使用分段的思想,來盡量平攤并發(fā)壓力(類似1.7及以前版本的 ConcurrentHashMap.Segment)。
最終依舊使用 compareAndSwapLong 來更新值。
/** * Padded variant of AtomicLong supporting only raw accesses plus CAS. * * JVM intrinsics note: It would be possible to use a release-only * form of CAS here, if it were provided. */ @sun.misc.Contended static final class Cell { volatile long value; Cell(long x) { value = x; } final boolean cas(long cmp, long val) { return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); } // Unsafe mechanics 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); } } }longAccumulate
將 Long 映射到 Cell[] 數(shù)組里面,通過 Hash 等算法映射到其中一個(gè)數(shù)字進(jìn)行計(jì)數(shù),而最終的計(jì)數(shù)結(jié)果就是其求和累加。在低并發(fā)的時(shí)候,通過對 base 的直接更新,可以很好地保證和 Atomic 性能的基本一致;而在高并發(fā)的時(shí)候,則將單點(diǎn)的更新壓力分散到各個(gè)節(jié)點(diǎn)上,提升了性能。
總結(jié)AtomicLong 適用于序號生成,這種情況下需要準(zhǔn)確的、全局唯一的數(shù)值;但在高并發(fā)情況下的計(jì)數(shù)操作,使用 AtomicLong 時(shí)會因線程競爭導(dǎo)致失敗白白循環(huán)一次;失敗次數(shù)越多,循環(huán)次數(shù)也越多。此時(shí)使用LongAdder 能更好地提升性能。
LongAdder 適用于高并發(fā)情況下的計(jì)數(shù)操作,利用與 JDK1.7 ConcurrentHashMap 相似的原理,以空間換時(shí)間,提高了實(shí)際的計(jì)數(shù)效率。當(dāng)然,線程競爭很低的情況下進(jìn)行計(jì)數(shù),使用 AtomicLong 還是更簡單更直接,并且效率稍微高一些。
注意:CAS 是 sun.misc.Unsafe 中提供的操作,只對 int、long、對象類型(引用或者指針)提供了這種操作,其他類型都需要轉(zhuǎn)化為這三種類型才能進(jìn)行 CAS 操作。(例如 DoubleAdder 就是 LongAdder 的簡單改造,主要的變化就是用 Double.longBitsToDouble 和 Double.doubleToRawLongBits 對底層的8字節(jié)數(shù)據(jù)進(jìn)行 long <=> double 轉(zhuǎn)換,存儲的時(shí)候使用 long 型,計(jì)算的時(shí)候轉(zhuǎn)化為 double 型。)
參考資料
談?wù)凜oncurrentHashMap1.7和1.8的不同實(shí)現(xiàn)
jdk1.8 LongAdder源碼學(xué)習(xí)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69085.html
摘要:在并發(fā)量較低的環(huán)境下,線程沖突的概率比較小,自旋的次數(shù)不會很多。比如有三個(gè),每個(gè)線程對增加。的核心方法還是通過例子來看假設(shè)現(xiàn)在有一個(gè)對象,四個(gè)線程同時(shí)對進(jìn)行累加操作。 showImg(https://segmentfault.com/img/remote/1460000016012084); 本文首發(fā)于一世流云的專欄:https://segmentfault.com/blog... ...
摘要:失敗重試自旋比如說,我上面用了個(gè)線程,對值進(jìn)行加。我們都知道如果在線程安全的情況下,這個(gè)值最終的結(jié)果一定是為的。那就意味著每個(gè)線程都會對這個(gè)值實(shí)質(zhì)地進(jìn)行加。 前言 只有光頭才能變強(qiáng) 之前已經(jīng)寫過多線程相關(guān)的文章了,有興趣的同學(xué)可以去了解一下: https://github.com/ZhongFuCheng3y/3y/blob/master/src/thread.md showImg(h...
摘要:原子類的作用多線程操作,性能開銷太大并不是原子操作。每次比較的是兩個(gè)對象性能比要好使用時(shí),在高并發(fā)下大量線程會同時(shí)去競爭更新同一個(gè)原子變量,但是由于同時(shí)只有一個(gè)線程的會成功,所以其他線程會不斷嘗試自旋嘗試操作,這會浪費(fèi)不少的資源。 AtomicInteger 原子類的作用 多線程操作,Synchronized 性能開銷太大count++并不是原子操作。因?yàn)閏ount++需要經(jīng)過讀取-...
摘要:在有些情況下,原子操作可以在不使用關(guān)鍵字和鎖的情況下解決多線程安全問題。但其內(nèi)部的結(jié)果不是一個(gè)單一的值這個(gè)類的內(nèi)部維護(hù)了一組變量來減少多線程的爭用。當(dāng)來自多線程的更新比讀取更頻繁時(shí)這個(gè)類往往優(yōu)于其他的原子類。 原文地址: Java 8 Concurrency Tutorial: Atomic Variables and ConcurrentMap AtomicInteger java...
閱讀 3215·2021-11-10 11:36
閱讀 3164·2021-11-02 14:39
閱讀 1745·2021-09-26 10:11
閱讀 4998·2021-09-22 15:57
閱讀 1703·2021-09-09 11:36
閱讀 2061·2019-08-30 12:56
閱讀 3503·2019-08-30 11:17
閱讀 1711·2019-08-29 17:17