1:為什么會出現Atomic類
在多線程或者並發環境中,我們常常會遇到這種情況 int i=0; i++ 稍有經驗的同學都知道這種寫法是線程不安全的。為了達到線程安全的目的,我們通常會用synchronized來修飾對應的代碼塊。現在我們有了新的方法,就是使用J.U.C包下的atomic類。
2:Atomic類的原理是什么呢
一句話來說,atomic類是通過自旋CAS操作volatile變量實現的。
CAS是compare and swap的縮寫,即比較后(比較內存中的舊值與預期值)交換(將舊值替換成預期值)。它是sun.misc包下Unsafe類提供的功能,需要底層硬件指令集的支撐。
使用volatile變量是為了多個線程間變量的值能及時同步。
3:為什么使用Atomic類
按理來說,使用synchroized已經能滿足功能需求了。為什么還會有這個類呢?那肯定是性能的問題了。
在JDK1.6之前,synchroized是重量級鎖,即操作被鎖的變量前就對對象加鎖,不管此對象會不會產生資源競爭。這屬於悲觀鎖的一種實現方式。
而CAS會比較內存中對象和當前對象的值是否相同,相同的話才會更新內存中的值,不同的話便會返回失敗。這是樂觀鎖的一中實現方式。這種方式就避免了直接使用內核狀態的重量級鎖。
但是在JDK1.6以后,synchronized進行了優化,引入了偏向鎖,輕量級鎖,其中也采用了CAS這種思想,效率有了很大的提升。
4:Atomic類的缺點
ABA問題:
對於一個舊的變量值A,線程2將A的值改成B又改成可A,此時線程1通過CAS看到A並沒有變化,但實際A已經發生了變化,這就是ABA問題。解決這個問題的方法很簡單,記錄一下變量的版本就可以了,在變量的值發生變化時對應的版本也做出相應的變化,然后CAS操作時比較一下版本就知道變量有沒有發生變化。atomic包下AtomicStampedReference類實現了這種思路。Mysql中Innodb的多版本並發鎖也是這個原理。
自旋問題:
atomic類會多次嘗試CAS操作直至成功或失敗,這個過程叫做自旋。通過自旋的過程我們可以看出自旋操作不會將線程掛起,從而避免了內核線程切換,但是自旋的過程也可以看做CPU死循環,會一直占用CPU資源。這種情形在單CPU的機器上是不能容忍的,因此自旋一般都會有個次數限制,即超過這個次數后線程就會放棄時間片,等待下次機會。因此自旋操作在資源競爭不激烈的情況下確實能提高效率,但是在資源競爭特別激烈的場景中,CAS操作會的失敗率就會大大提高,這時使用中重量級鎖的效率可能會更高。當前,也可以使用LongAdder類來替換,它則采用了分段鎖的思想來解決並發競爭的問題。