Java并发编程包中atomic的实现原理
轉載自? ?Java并發(fā)編程包中atomic的實現(xiàn)原理
這是一篇來自粉絲的投稿,作者【林灣村龍貓】最近在閱讀Java源碼,這一篇是他關于并發(fā)包中atomic類的源碼閱讀的總結。Hollis做了一點點修改。
?
引子
在多線程的場景中,我們需要保證數(shù)據(jù)安全,就會考慮同步的方案,通常會使用synchronized或者lock來處理,使用了synchronized意味著內核態(tài)的一次切換。這是一個很重的操作。
有沒有一種方式,可以比較便利的實現(xiàn)一些簡單的數(shù)據(jù)同步,比如計數(shù)器等等。concurrent包下的atomic提供我們這么一種輕量級的數(shù)據(jù)同步的選擇。
?
使用例子
import?java.util.concurrent.CountDownLatch; import?java.util.concurrent.atomic.AtomicInteger;public?class?App?{public?static?void?main(String[]?args)?throws?Exception?{CountDownLatch?countDownLatch?=?new?CountDownLatch(100);AtomicInteger?atomicInteger?=?new?AtomicInteger(0);for?(int?i?=?0;?i?<?100;?i++)?{new?Thread()?{@Overridepublic?void?run()?{atomicInteger.getAndIncrement();countDownLatch.countDown();}}.start();}countDownLatch.await();System.out.println(atomicInteger.get());} }在以上代碼中,使用AtomicInteger聲明了一個全局變量,并且在多線程中進行自增,代碼中并沒有進行顯示的加鎖。
以上代碼的輸出結果,永遠都是100。如果將AtomicInteger換成Integer,打印結果基本都是小于100。
也就說明AtomicInteger聲明的變量,在多線程場景中的自增操作是可以保證線程安全的。接下來我們分析下其原理。
?
原理
我們可以看一下AtomicInteger的代碼
他的值是存在一個volatile的int里面。volatile只能保證這個變量的可見性。不能保證他的原子性。
可以看看getAndIncrement這個類似i++的函數(shù),可以發(fā)現(xiàn),是調用了UnSafe中的getAndAddInt。
UnSafe是何方神圣?UnSafe提供了java可以直接操作底層的能力。
進一步,我們可以發(fā)現(xiàn)實現(xiàn)方式:
如何保證原子性:自旋 +?CAS(樂觀鎖)。在這個過程中,通過compareAndSwapInt比較更新value值,如果更新失敗,重新獲取舊值,然后更新。
?
優(yōu)缺點
CAS相對于其他鎖,不會進行內核態(tài)操作,有著一些性能的提升。但同時引入自旋,當鎖競爭較大的時候,自旋次數(shù)會增多。cpu資源會消耗很高。
換句話說,CAS+自旋適合使用在低并發(fā)有同步數(shù)據(jù)的應用場景。
Java 8做出的改進和努力
在Java 8中引入了4個新的計數(shù)器類型,LongAdder、LongAccumulator、DoubleAdder、DoubleAccumulator。他們都是繼承于Striped64。
在LongAdder 與AtomicLong有什么區(qū)別?
Atomic*遇到的問題是,只能運用于低并發(fā)場景。因此LongAddr在這基礎上引入了分段鎖的概念??梢詤⒖肌禞DK8系列之LongAdder解析》一起看看做了什么。
大概就是當競爭不激烈的時候,所有線程都是通過CAS對同一個變量(Base)進行修改,當競爭激烈的時候,會將根據(jù)當前線程哈希到對于Cell上進行修改(多段鎖)。
?
可以看到大概實現(xiàn)原理是:通過CAS樂觀鎖保證原子性,通過自旋保證當次修改的最終修改成功,通過降低鎖粒度(多段鎖)增加并發(fā)性能。
總結
以上是生活随笔為你收集整理的Java并发编程包中atomic的实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么创自己的网页(怎么创自己的网页链接)
- 下一篇: Java中的List你真的会用吗