1. 程式人生 > >【Java多線程】AtomicLong和LongAdder

【Java多線程】AtomicLong和LongAdder

fan cor 新的 fps get 答案 ext 進行 方式

AtomicLong簡要介紹

AtomicLong是作用是對長整形進行原子操作,顯而易見,在java1.8中新加入了一個新的原子類LongAdder,該類也可以保證Long類型操作的原子性,相對於AtomicLong,LongAdder有著更高的性能和更好的表現,可以完全替代AtomicLong的來進行原子操作。
在32位操作系統中,64位的long 和 double 變量由於會被JVM當作兩個分離的32位來進行操作,所以不具有原子性。而使用AtomicLong能讓long的操作保持原子型。

AtomicLong的實現方式是內部有個value 變量,當多線程並發自增,自減時,均通過cas 指令從機器指令級別操作保證並發的原子性。

唯一會制約AtomicLong高效的原因是高並發,高並發意味著CAS的失敗幾率更高, 重試次數更多,越多線程重試,CAS失敗幾率又越高,變成惡性循環,AtomicLong效率降低。 那怎麽解決?

LongAdder給了我們一個非常容易想到的解決方案: 減少並發,將單一value的更新壓力分擔到多個value中去,降低單個value的 “熱度”,分段更新!!! 這樣,線程數再多也會分擔到多個value上去更新,只需要增加value就可以降低 value的 “熱度” AtomicLong中的 惡性循環不就解決了嗎? cells 就是這個 “段” cell中的value 就是存放更新值的, 這樣,當我需要總數時,把cells 中的value都累加一下不就可以了麽!!

當然,聰明之處遠遠不僅僅這裏,在看看add方法中的代碼,casBase方法可不可以不要,直接分段更新,上來就計算 索引位置,然後更新value?

答案是不好,不是不行,因為,casBase操作等價於AtomicLong中的cas操作,要知道,LongAdder這樣的處理方式是有壞處的,分段操作必然帶來空間上的浪費,可以空間換時間,但是,能不換就不換,看空間時間都節約~! 所以,casBase操作保證了在低並發時,不會立即進入分支做分段更新操作,因為低並發時,casBase操作基本都會成功,只有並發高到一定程度了,才會進入分支,所以,Doug Lead對該類的說明是: 低並發時LongAdder和AtomicLong性能差不多,高並發時LongAdder更高效!

但是,Doung Lea 還是沒這麽簡單,聰明之處還沒有結束……

如此,retryUpdate中做了什麽事,也基本略知一二了,因為cell中的value都更新失敗(說明該索引到這個cell的線程也很多,並發也很高時) 或者cells數組為空時才會調用retryUpdate,

因此,retryUpdate裏面應該會做兩件事:

1. 擴容,將cells數組擴大,降低每個cell的並發量,同樣,這也意味著cells數組的rehash動作。

2. 給空的cells變量賦一個新的Cell數組。

LongAdder確實用了很多心思減少並發量,並且,每一步都是在”沒有更好的辦法“的時候才會選擇更大開銷的操作,從而盡可能的用最最簡單的辦法去完成操作。追求簡單,但是絕對不粗暴。

AtomicLong可不可以廢掉?
我的想法是可以廢掉了,因為,雖然LongAdder在空間上占用略大,但是,它的性能已經足以說明一切了,無論是從節約空的角度還是執行效率上,AtomicLong基本沒有優勢了,具體看這個測試(感謝coolshell讀者Lemon的回復):http://blog.palominolabs.com/2014/02/10/java-8-performance-improvements-longadder-vs-atomiclong/

轉自:https://blog.csdn.net/wangxiaotongfan/article/details/51745506?locationNum=1&fps=1


【Java多線程】AtomicLong和LongAdder