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