Java併發編程之CAS二源碼追根溯源java
在上一篇文章中,咱們知道了什麼是CAS以及CAS的執行流程,在本篇文章中,咱們將跟着源碼一步一步的查看CAS最底層實現原理。編程
本篇是《凱哥(凱哥Java:kagejava)併發編程學習》系列之《CAS系列》教程的第二篇:從源碼追根溯源查看CAS最底層是怎麼實現的。安全
本文主要內容:CAS追根溯源,完全找到CAS的根在哪裏。併發
經過上一篇文章學習,咱們知道了AtomicInteger.compareAndSet方法不加鎖能夠保證原子性(其原理就是unsafe+cas實現的),咱們來看看其源碼:ide
AtomicInteger對象(下文凱哥簡稱:atoInteger)怎麼保證變量內存可見性呢?學習
查看源碼:this
咱們來看看atoInteger的compareAndSet方法。凱哥在上面添加了註釋。spa
在調用unsafe的compareAndSwapInt這個方法的時候,unsafe是什麼?this指的是什麼?valueOffset又是什麼呢?操作系統
咱們接着查看atoInteger源碼:線程
咱們發現Unsafe以及valueOffset都是從一個對象中獲取到的。
那麼this指的是什麼?其實this就是當前atoInteger對象。
那麼Unsafe對象在哪裏呢?
咱們想要看源碼,怎麼查看呢?發現不能看源碼啊。別急,這個文件的源碼能夠從openJdk的源碼中查到。
接着,咱們來查看OpenJdk8的源碼:
(PS:下載OpenJdk8源碼凱哥這裏就不贅述了。在文章最後,凱哥給出)
下載完,解壓以後,文件位置:openjdk\jdk\src\share\classes\sun\misc。以下圖:
咱們來看看Unsafe類上面的註解:
A collection of methods for performing low-level, unsafe operations.
什麼意思呢?用於執行底層的(low-level,)、不安全操做的方法的集合。
就是說,這個類能夠直接操做底層數據的。
須要說明的是:在這個對象中大量的方法使用了native來修飾(據網友統計高達82個)
咱們知道,Java的方法使用native關鍵字修飾的,說明這個方法不是Java自身的方法(非Java方法),可能調用的是其餘語言的。如C或C++語言的方法。
咱們再來看看:unsafe.objectFieldOffse()中的
這個方法就是返回一個內存中訪問偏移量。
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
在unsafe類中compareAndSwapInt方法也是native的。咱們在來看看這個方法調用操做系統底層C++的代碼:
說明:
jint *addr:主內存中的變量值
old:對象工做區域的值
new_val:將要改變的值。
這三個是否是很熟悉,對。就是CAS的三個參數。
分析第13行爲何返回false:
在11行的時候,設置主內存的變量值V=1.
在12行後,更新爲V=2020了。
當執行到第13行的時候,
主內存:V=2020
程序工做區變量值jint *addr A的值:A=1
new_val:1024。
從調用C++代碼咱們能夠分析到:
在第5行的時候,由於1!=2020,因此return 的result就是false.
因此第13行輸出的是false.
調用的是getAndAddInt方法。接着查看unsafe的源碼,就會發現CAS保證原子性的終極代碼。
看看:getObjectVolatile。方法發現是native.以下圖:
再來看看compareAndSwapObject:
發現是native修飾的方法。說明不是Java的方法。這個咱們等會再細說。
源碼:
咱們來模擬:atoInteger.getAndIncrement();
假設默認值是0. 主內存的值是0
在調用getAndSetObject方法的幾個參數說明:
Var1:當前atoInteger對象
Var2:當前偏移量(內存地址所在位置。如:三排四列)
Vart4:默認就是1
Var5:獲取到的主內存的值
Var5+var4:將要更新的值。
從源碼,咱們看到是do while語句。爲何不是while語句呢?由於先要獲取到主內存中變量最新的值,而後再判斷。因此選用了do while語句。
咱們來看看當CPU1線程1和CPU2線程B來執行的時候:
兩個線程都從主內存copay了i的值到本身工做內存空間後,進行+1的操做。
假設線程1再執行+1操做後,準備往主內存回寫數據的時候,CPU1被掛起。而後CPU2競爭到資源以後,也操做i+1後,將更新後的值回寫到了主內存中。而後切換到CPU1了,CPU1接着執行。對比代碼分析:
線程1在執行do後獲得的值var5=1而不是0
而後while裏面執行:var1和var2運算後的結果是0(工做區的值)。
由於0!=5 .因此this.comparAndSwapInt的值是false.
又由於前面有個! 非得符號。也就是!false。咱們知道!false就是true.
也就是while(true)。While(true)後,接着循環執行。線程會放棄原有操做,從新從主內存中獲取到最新數據(此時就是1了),而後再進行操做後。
又到了do,獲取在主內存最新數據是1.接着走while()
由於,var1,var2獲取到工做區的值是1 var5也等於1.1=1,成立了,執行var5+var5=1+1=2,來更新主內存的數據後返回true.
又由於前面有個!非的符號。因此就是while(!true),也就是while(false)。退出循環,返回var5的值。
經過上面的運行分析,咱們發現atoInteger的getAndIncrement方法保證原子性是unsafe+CAS來保證變量原子性的(其中do while語句就是後面咱們將要學到的自旋)