串一串java的voliate,cas,原子類

cpu是如何使用內存的

談voliate之前就不得不談一談cpu和們的主存之間的關係。CPU的運算處理速度與內存讀寫速度的差異非常巨大,爲了解決這種差異充分利用CPU的使用效率,就開始在CPU處理器和內存之間加了一層緩衝區。
注意:所以所謂的本地內存,主內存都是抽象概念,並不一定就真實的對應cpu緩存和物理內存。當然如果是出於理解的目的,這樣對應起來也無不可。
在這裏插入圖片描述
但是高速緩存在解決cpu的內存速度不一致問題的同時,也引起了新的問題,那就是共享數據的不一致性。解決這個問題最簡單的方法是用Synchronized關鍵字。Synchronized關鍵字能同時解決程序執行的“原子性”,“可見性”,和“有序性”。當我們獲得鎖的時候,執行同步代碼,線程會被強制從主內存中讀取數據,先把主內存的數據複製到本地內存,然後在本地內存進行修改,在釋放鎖的時候,會把數據寫回主內存。但是這樣做的效率無異會很低。這時候就引入了voliate

voliate

前面說了,如果要解決共享數據的安全性,要保證三個條件:可見性,有序性 和 原子性。
voliate能保證可見性和有序性

  1. 可見性保證:

    • CPU對volatile修飾變量的內存副本修改會立刻同步回主內存。
    • 對其他核心立即可見,這個的意思是,如果其他核心如果緩存了被修改的volatile修飾變量,那其他核心副本就會立刻失效。然後從主存中獲取最新的數據。
  2. 有序性保證:
    主要就是防止指令重排。有volatile修飾的變量,賦值操作後加內存屏障,防止內存屏障後面的語句重排到賦值操作之前。防止在拿到變量之後賦值操作還沒完成。
    舉個例子:
    在單例模式中,Instance inst = new Instance(); 這一句,就不是原子操作,它可以分成三步原子指令:
    1,分配內存地址;
    2,new一個Instance對象;(這裏加內存屏障)
    3,將內存地址賦值給inst;

但是voliate是沒法保證原子性,例如像n++這種組合操作。
針對num++這類複合類的操作,可以使用java併發包中的原子操作類原子操作類是通過循環CAS的方式來保證其原子性的。

CAS方式

CAS指令執行時,當且僅當內存地址V的值與預期值A相等時,將內存地址V的值修改爲B,否則就什麼都不做。整個比較並替換的操作是一個原子操作。
CAS方式的原子性在api層面是無法實現的。因爲判斷相等到值替換中間我們的數據有可能被其他線程更改。所以cas實際上是從硬件層面或者說cpu層面來實現的。

這個操作主要是通過:c++編寫的Atomic::cmpxchg指令實現的,這個指令在不同架構的cpu下實現不一樣。使用該方法的大體上邏輯如下:
1.通過判斷是否是多核cpu來給決定是否給該方法上鎖標誌。因爲單核情況下一次只有一個線程能運行,在判斷和賦值中間一般不會發生中斷而導致切換線程,所以不用擔心屬數據被更改。
2. 如果遇到帶有lock前綴的指令,那在此期間會鎖定從cpu通往內存的總線(總線鎖定)。但是鎖定總線代價較高,其他cpu上運行的線程即便訪問的不是共享數據,也是無法通過總線來訪問數據。爲此引入了緩存鎖定,只需要鎖定緩存在cpu本地內存數據對應的主內存數據即可。

voliate和cas機制的典型應用——原子類

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章