怎樣正確理解volatile?

是一哥嘛發表於2019-08-29

volatile是java虛擬機器提供的輕量級的同步機制

三大特徵:

(1)保證可見性

當執行緒對變數進行操作時,必須在工作記憶體中進行,完成後再寫會到主記憶體中。

在寫回主記憶體中後其他執行緒馬上都能看到,就是 可見性

(2)不保證原子性

丟失寫值的情況

(3)禁止指令重拍


JMM記憶體模型

可見性

原子性

有序性

主記憶體:主記憶體是共享記憶體的區域,相當於我們的記憶體條,裡面儲存共享變數,所有執行緒都能訪問

工作記憶體:每個執行緒建立時JVM都會為其建立一個工作記憶體,工作記憶體裡面存放的是該執行緒的私有資料,不能相互訪問,

當執行緒對變數進行操作時,必須在工作記憶體中進行,完成後再寫會到主記憶體中。

在寫回主記憶體中後其他執行緒馬上都能看到,就是可見性


什麼是CAS(compareAndSet)?

比較並交換


AtomicInteger.compareAndSet(期望值,要修改的值)

期望值:從主實體記憶體中的值

執行緒每次修改值的時候都要從主實體記憶體中去獲取並比較是否和期望值相同,如果和期望值相同,則修改成要修改的值,並返回true


CAS底層原理?

unsafe valueoffset(記憶體偏移地址)

Unsafe是CAS的核心類

atomicInteger.getAndIncrement();
底層原始碼:::::
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}

假設 執行緒A 和 執行緒B 同時執行getAndAddInt操作,

首先執行緒A先從主記憶體中獲取到變數A=3到自己的執行緒記憶體中,

同時執行緒B也從主記憶體中獲取到變數A=3到自己的執行緒記憶體中,

現在AB執行緒中都有一份自己的記憶體變數A=3的副本,


執行緒A透過this.getIntVolatile(var1, var2);

獲取到記憶體中的A=3,這時執行緒A被掛起。


這時執行緒B透過this.getIntVolatile(var1, var2);

獲取到記憶體中的A=3,並執行了this.compareAndSwapInt(var1, var2, var5, var5 + var4)

比較主記憶體中的值也為3,就成功將自己的工作記憶體中的A修改為4並寫回到主記憶體中。


這時執行緒A回覆,執行compareAndSwapIn方法比較,發現自己工作記憶體中的A與主記憶體中的A

不一致,說明已經被其他執行緒修改過了,那執行緒A本次修改失敗,while重新來一遍


執行緒A重新獲取value,因為變數被volatile修飾,所以其他執行緒對他的修改執行緒A總是能看到

(volatile的可見性原理)執行緒A繼續執行compareAndSwapInt進行比較直到成功

缺點:

迴圈時間開銷很大

只能保證一個共享變數的原子操作

ABA問題(狸貓換太子)

CAS演算法是提取記憶體中某時刻的資料並在當下時刻進行比較並替換,這個時間差會導致資料變化

比如T1從記憶體位置V中取出A,這時另一個執行緒T2也取出A,T2 將A–>B,然後T2又將B–>A,這

時T1進行CAS發現記憶體中還是A,然後執行緒T1就操作成功了


解決ABA問題:

原子引用AtomicReference

新增一種機制,修改版本號 AtomicStampedReference(初始值,初始版本號)

atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946594/viewspace-2655460/,如需轉載,請註明出處,否則將追究法律責任。

相關文章