volatile
功能
- 保證多線程的可見性
- 禁止一部分的重排序。
- volatile 是輕量級的synchronized
- 對任意單個的volatile的讀/寫是原子性的(volatile=1/return volatile),但是複合型操作不支持。(volatile++操作)
特性
對於volatile來說具有以下特性
1.volatile標記的變量在讀操作的時候,一定是最新的值。
爲什麼?
1、lock前綴的支持,volatile規定每次修改操作必須刷新到內存,讀操作也要到內存中去取新值。
2、總線的支持,多條總線事務同一時刻同一時刻只有一條能獲得訪問內存的權限,因此,只要有線程修改了,那麼讀操作一定取到的是新值。
####內存語意
讀:當讀一個volatile變量時,JMM會把該線程對應的本地內存置爲無效。線程接下來將從主內存中讀取共享變
寫:當寫一個volatile變量時,JMM會把該線程對應的本地內存中的共享變量值刷新到主內
原理
instance = new Singleton(); // instance是volatile變量
0x01a3de1d: movb $0×0,0×1104800(%esi);0x01a3de24: **lock** addl $0×0,(%esp);
lock前綴的指令在多核處理器下會觸發兩種操作
- 將當前處理器緩存行的數據寫回到系統內存。
- 這個寫回內存的操作會使在其他CPU裏緩存了該內存地址的數據無效。
依賴CPU的緩存一致性的支持
volatile規則表
當第二個操作是volatile寫時,不管第一個操作是什麼,都不能重排序。這個規則確保volatile寫之前的操作不會被編譯器重排序到volatile寫之後。
當第一個操作是volatile讀時,不管第二個操作是什麼,都不能重排序。這個規則確保volatile讀之後的操作不會被編譯器重排序到volatile讀之前。
當第一個操作是volatile寫,第二個操作是volatile讀時,不能重排
在每個volatile寫操作的前面插入一個StoreStore屏障。
在每個volatile寫操作的後面插入一個StoreLoad屏障。
在每個volatile讀操作的後面插入一個LoadLoad屏障。
在每個volatile讀操作的後面插入一個LoadStore屏障。
synchronized
- 原子性。
- 由於線程同步,某一時刻,只能一個線程操作共享變量,也就保證了多線程間的可見性
使用
- 普通同步方法:鎖是this鎖
- 代碼塊:鎖是該對象鎖
- 靜態同步方法:鎖是當前Class對象鎖
原理
- 都是使用Monitor(監視器)對象來操作的
monitorenter在編譯時插入到synchronized的開始位置,monitorexit在編譯時插入在synchronized的結束位置和者異常結束的位置。 - 任何對象都是鎖對象,那麼任何對象都與一個monitor相關聯,當獲取到monitor,該monitor就會處於鎖定狀態。當指令執行到monitorenter時,就會去嘗試獲取該monitor的所有權。
原子性
- 通過鎖來實現一些列操作的原子性。但是性能差,會造成阻塞,增加性能消耗。
- 通過CAS(compare and swap)來實現,此實現針對於賦值操作。i++,
- 通過E(期望值,也就是緩存/本地內存/工作內存的副本),V(當前值)來操作。通過E和主內存的值進行比較,如果相等的話,就會將新值賦值給當前值。
- 問題,會造成ABA的問題,當其他線程將共享變量的值變換爲B,然後再該爲A,那麼該線程去將本地內存的值刷到主內存的時候,將會執行成功。但是結果可能是錯的。
- 通過CAS+version的方式來解決ABA的問題。
- 也就是,共享變量V的值爲 1A->2B->3A 那麼通過此版本號去控制,就不會巧妙的去除ABA問題。
- 注意,CAS只能處處理一個變量,如果多個可以使用ij = 1a的方式,AtomicReference保證引用對象的原子性。
- CAS 不需要添加鎖,但是循環驗證會增加CPU的消耗。
核心代碼
- CAS實現操作,CASXXXX方法就是CAS操作。Unsafe.class 操作c代碼。
do{
N = E+1;
}while(CASXXXX((E,N));
java concurrent包下的atomic類
AtomicInteger AtomicBoolean...(CAS,會出現BAB的問題)
AtomicStampedReference 解決了CAS的ABA的問題