17.AtomicInteger、AtomicBoolean的底层原理
小陳:老王啊,今天就要開始Atomic原子類的學習了吧......
老王:是啊,之前我們只是簡單介紹了Atomic的體系,今天我們就要進入Atomic底層原理的的學習了,首先我們從AtomicInteger這個比較簡單的原子類開始,在說AtomicInteger的底層原理之前呢,我先給你看兩個例子:
實測樣例對比Integer和AtomicInteger的線程安全性
Integer的測試樣例
(1)定義一個共享變量Integer
(2)定義一個新的線程類,創建兩個線程,每個線程執行10000次value++操作
public class AddDemo {// 定義一個Integer類型的共享變量valueprivate static Integer value = 0;public static class AddThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 10000; i++) {value++;}}}public static void main(String[] args) throws InterruptedException {// 定義兩個線程各自對value執行10000次自增操作AddThread addThread1 = new AddThread();AddThread addThread2 = new AddThread();// 啟動兩個線程addThread1.start();addThread2.start();// 主線程等待兩個線程執行完畢addThread1.join();addThread2.join();// 輸出最新的value結果System.out.println("value的值為:" + value);} }看看最后得到的結果19513,比預期20000相差還是挺大的
AtomicInteger的測試樣例
(1)定義一個AtomicInteger原子類
(2)定義一個新的線程類AtomicAddThread,創建兩個線程,每個線程執行10000次incrementAndGet()操作
public class AtomicAddDemo {private static AtomicInteger value = new AtomicInteger(0);public static class AtomicAddThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 10000; i++) {value.incrementAndGet();}}}public static void main(String[] args) throws InterruptedException {// 定義兩個線程各自對value執行10000次自增操作AtomicAddThread atomicAddThread1 = new AtomicAddThread();AtomicAddThread atomicAddThread2 = new AtomicAddThread();// 啟動兩個線程atomicAddThread1.start();atomicAddThread2.start();// 主線程等待兩個線程執行完畢atomicAddThread1.join();atomicAddThread2.join();// 輸出最新的value結果System.out.println("value的值為:" + value.get());} }實際的結果20000,與預期的結果準確無誤
老王:小陳啊,通過上述的實際例子,說明 AtomicInteger原子類確實是線程安全的。
小陳:是啊,使用AtomicInteger兩個線程執行20000次自增操作得到的結果于預期值一致,那AtomicInteger底層到底是怎么確保線程安全的呢?
老王:這個啊,我們慢慢來剖析......
AtomicInteger的內部屬性
老王:我們先通過源碼來看一下AtomicInteger內部有哪些屬性以及作用是什么:
public class AtomicInteger extends Number implements java.io.Serializable {// unsafe對象,可以直接根據內存地址操作數據,可以突破java語法的限制private static final Unsafe unsafe = Unsafe.getUnsafe();// 存儲實際的值private volatile int value;// 存儲value屬性在AtomicInteger類實例內部的偏移地址private static final long valueOffset;static {try {// 在類初始化的時候就獲取到了value變量在對象內部的偏移地址valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }} }(1)首先內部持有一個unsafe對象,Atomic原子類底層的操作都是基于unsafe對象來進行的
(2)然后有一個volatile int value變量,這個value就是原子類的實際數值,使用volatile來修飾,volatile可以保證并發中的可見性和有序性(這里之前講過volatile可以保證可見性和有序性,不記得的要回去重新看一下哦)
(3)還有一個valueOffset,看看這段代碼,其實就是獲得value屬性在AtomicInteger對象內部的偏移地址的:
valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));這個value屬性相對于AtomicInter對象的內部偏移量存儲在valueOffset中,我們之前講過的,通過unsafe類是直接在內存級別去給變量賦值的。這里啊,我們再回顧一下unsafe值怎么從內存級別操作數據的:
- 首先要知道你要操作對象的內存地址,也就是AtomicInteger對象引用指向的內存地址
- 其次是要知道value屬性在對象內部的偏移量offset,就可以通過(對象地址 + offset偏移量)直接找到value變量在內存的地址是多少,然后就可以直接給這塊內存賦值了。
小陳:額,這個AtomicInteger內部還是蠻簡單的呀,一個 volatile int value的屬性、一個unsafe類、一個偏移地址就完事了
老王:哈哈,是啊,其實Atomic原子類啊,就是對基礎的類型進行了一下包裝而已,使得他們是線程安全的。比如AtomicInteger要對int進行包裝,所以它內部肯定是有一個屬性來存儲int的值的。至于它其他兩個屬性valueOffset、unsafe是輔助實現并發安全的屬性。
AtomicInteger的構造方法
老王:讓我們再來看看AtomicInteger的構造方法源碼:
public AtomicInteger(int initialValue) {value = initialValue; }public AtomicInteger() { }提供了兩個構造方法,第一個是在創建AtomicInteger對象的時候直接給內存存儲值的volatile int value設置初始化的值;第二個沒有賦初始值,那默認就是0
AtomicInteger方法的源碼分析
getAndIncrement()方法源碼
public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1); }我們看到AtomicInteger的getAndIncrement()方法源碼很簡單,底層就是基于unsafe.getAndAddInt包裝了一下,讓我們繼續看一下unsafe.getAndAddInt方法源碼:
public final int getAndAddInt(Object o, long valueOffset, int x) {int expected;do {expected = this.getIntVolatile(o, valueOffset);} while(!this.compareAndSwapInt(o, valueOffset, expected, expected + x));return expected; }(1)首先(o + valueOffset)得到value變量在內存中的地址,然后根據地址直接取出value在主內存值,這個值記錄為expected中
(2)根據 (o + offsetSet)地址偏移量,expected期待的值跟當前內存的值進行對比,如果相等則CAS操作成功,內存的值修改為 expected + x
(3)如果值不相等,則進入下一次循環,直到CAS操作成功為止。
(4)由于使用了volatile 修飾符修飾了value,所以一旦修改了別的線程能立馬可見、同時volatile還是用內存屏障確保有序性
(5)所以上面的CAS操作確保了原子性,通過volatile確保可見性、有序性;線程安全的三個特性都滿足了,上面的操作就是線程安全的。
小陳:原來這里AtomicInteger底層執行getAndIncrement()操作底層就是直接調用unsafe的getAndAddInt()方法啊,最后還是走到了unsafe的compareAndSwapInt方法里面了,這里還是簡單的呀。
老王:哈哈,AtomicInteger底層的源碼本來就是不難的,底層都是基于unsafe進行薄薄的包裝了一層而已,然后底層都是基于unsafe的CAS操作來保證原子性的,然后有使用volatile來修飾變量,保證了可見性和有序性,這樣它就是線程安全的。
老王:關于unsafe的CAS操作是怎么保證原子性的,小陳你還記得住不,前兩章的時候我們還畫了一個圖的:
小陳:嗯嗯,這個我記得的。
老王:好,那我也就不在CAS怎么保證原子性的話題上多說的了,我們繼續看AtomicInteger原子類的其它源碼:
AtomicInteger的compareAndSet源碼:
public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }底層也還是直接調用unsafe的compareAndSwapInt方法直接去修改,不過這里不同的是,只會執行一次CAS操作,即使失敗了也不會重復CAS
其它方法源碼:
其它的方法,基本都是直接調用unsafe.getAndInt方法,上面我們分析過了
public final int getAndDecrement() {return unsafe.getAndAddInt(this, valueOffset, -1); }public final int getAndAdd(int delta) {return unsafe.getAndAddInt(this, valueOffset, delta); }老王:好了,AtomicInteger的源碼基本就分析到這里了,小陳關于AtomicInteger的底層原理這塊,你還有其它的疑問不?
小陳:基本上沒有了,AtomicInteger的底層還是比較簡單的,基本都是調用unsafe的CAS操作確保原子性,然后使用volatile修飾變量,確保可見性和有序性,我理解上應該沒問題了。
老王:好的,那我們就進入下一個原子類AtomicBoolean的討論
AtomicBoolean 底層原理分析
AtomicBoolean 屬性
public class AtomicBoolean implements java.io.Serializable {// unsafe對象,可以直接根據內存地址操作數據,可以突破java語法的限制private static final Unsafe unsafe = Unsafe.getUnsafe();// 存儲實際的值private volatile int value;// 存儲value屬性在AtomicInteger類實例內部的偏移地址private static final long valueOffset;static {try {// 在類初始化的時候就獲取到了value變量在對象內部的偏移地址valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }} }小陳:啊這AtomicBoolean 擁有的屬性怎么跟AtomicInteger是一模一樣的!!,它不是布爾類型嗎?怎么使用一個int類型的 volatile int value來存儲?
老王:其實啊,這只是AtomicBoolean 玩的一個小把戲,我們接著看就知道了:
我們看一下AtomicBoolean的構造函數源碼:
public AtomicBoolean(boolean initialValue) {// 當傳入initialValue為true的時候value = 1 , false的時候value = 0value = initialValue ? 1 : 0; }所以這里我們猜測,AtomicBoolean 底層就是使用一個int類型來表示true和false的,當value = 1的時候表示true,當value = 0的時候表示false。
然后繼續看一下get()的源碼:
直接就是判斷value != 0 , 當value = 1則返回true,value = 0 返回false,證明了上面的猜想
public final boolean get() {return value != 0; }然后再看一下AtomicBoolean最常用最重要的方法compareAndSet源碼:
public final boolean compareAndSet(boolean expect, boolean update) {int e = expect ? 1 : 0;int u = update ? 1 : 0;return unsafe.compareAndSwapInt(this, valueOffset, e, u); }底層就是將true 轉成 1,將false轉成 0,然后還是調用unsafe的compareAndSwapInt方法去執行CAS操作!!,這個我們在上面將AtomicInteger的時候已經講過了
小陳:哎呀,原來是這樣啊,這個AtomicBoolean 耍花樣啊,我還以為它底層使用布爾類型來存儲值呢,哪知道這兄弟直接volatile 修飾的int類型,然后1 表示 true,0 表示false,這操作不都跟AtomicInteger一樣嗎?只是將value表示的意思換了一下而已......
老王:是啊,看過AtomicBoolean的底層源碼之后恍然大悟了吧,很多功能啊其實實現起來沒有那么難,還是有很多的方式的.....
小陳:恩恩,這個我認同......
老王:小陳啊,今天我們將AtomicInteger、AtomicBoolean 的底層原理就到這里了,我們明天繼續......
小陳:我們下一章見。
關注小陳,公眾號上更多更全的文章
JAVA并發文章目錄(公眾號)
JAVA并發專題 《筑基篇》
1.什么是CPU多級緩存模型?
2.什么是JAVA內存模型?
3.線程安全之可見性、有序性、原子性是什么?
4.什么是MESI緩存一致性協議?怎么解決并發的可見性問題?
JAVA并發專題《練氣篇》
5.volatile怎么保證可見性?
6.什么是內存屏障?具有什么作用?
7.volatile怎么通過內存屏障保證可見性和有序性?
8.volatile為啥不能保證原子性?
9.synchronized是個啥東西?應該怎么使用?
10.synchronized底層之monitor、對象頭、Mark Word?
11.synchronized底層是怎么通過monitor進行加鎖的?
12.synchronized的鎖重入、鎖消除、鎖升級原理?無鎖、偏向鎖、輕量級鎖、自旋、重量級鎖
13.synchronized怎么保證可見性、有序性、原子性?
JAVA并發專題《結丹篇》
14. JDK底層Unsafe類是個啥東西?
15.unsafe類的CAS是怎么保證原子性的?
16.Atomic原子類體系講解
17.AtomicInteger、AtomicBoolean的底層原理
18.AtomicReference、AtomicStampReference底層原理
19.Atomic中的LongAdder底層原理之分段鎖機制
20.Atmoic系列Strimped64分段鎖底層實現源碼剖析
JAVA并發專題《金丹篇》
21.AQS是個啥?為啥說它是JAVA并發工具基礎框架?
22.基于AQS的互斥鎖底層源碼深度剖析
23.基于AQS的共享鎖底層源碼深度剖析
24.ReentrantLock是怎么基于AQS實現獨占鎖的?
25.ReentrantLock的Condition機制底層源碼剖析
26.CountDownLatch 門栓底層源碼和實現機制深度剖析
27.CyclicBarrier 柵欄底層源碼和實現機制深度剖析
28.Semaphore 信號量底層源碼和實現機深度剖析
29.ReentrantReadWriteLock 讀寫鎖怎么表示?
30. ReentrantReadWriteLock 讀寫鎖底層源碼和機制深度剖析
JAVA并發專題《元神篇》并發數據結構篇
31.CopyOnAarrayList 底層分析,怎么通過寫時復制副本,提升并發性能?
32.ConcurrentLinkedQueue 底層分析,CAS 無鎖化操作提升并發性能?
33.ConcurrentHashMap詳解,底層怎么通過分段鎖提升并發性能?
34.LinkedBlockedQueue 阻塞隊列怎么通過ReentrantLock和Condition實現?
35.ArrayBlockedQueued 阻塞隊列實現思路竟然和LinkedBlockedQueue一樣?
36.DelayQueue 底層源碼剖析,延時隊列怎么實現?
37.SynchronousQueue底層原理解析
JAVA并發專題《飛升篇》線程池底層深度剖析
38. 什么是線程池?看看JDK提供了哪些默認的線程池?底層竟然都是基于ThreadPoolExecutor的?
39.ThreadPoolExecutor 構造函數有哪些參數?這些參數分別表示什么意思?
40.內部有哪些變量,怎么表示線程池狀態和線程數,看看道格.李大神是怎么設計的?
41. ThreadPoolExecutor execute執行流程?怎么進行任務提交的?addWorker方法干了啥?什么是workder?
42. ThreadPoolExecutor execute執行流程?何時將任務提交到阻塞隊列? 阻塞隊列滿會發生什么?
43. ThreadPoolExecutor 中的Worker是如何執行提交到線程池的任務的?多余Worker怎么在超出空閑時間后被干掉的?
44. ThreadPoolExecutor shutdown、shutdownNow內部核心流程
45. 再回頭看看為啥不推薦Executors提供幾種線程池?
46. ThreadPoolExecutor線程池篇總結
總結
以上是生活随笔為你收集整理的17.AtomicInteger、AtomicBoolean的底层原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信小程序接口
- 下一篇: 易语言:置随机数种子这个命令有什么用?