章節(jié)目錄
- 1.volatile 的特性
- 為什么volatile修飾變量的寫操作不是原子性的?
- 2.volatile 寫-讀建立的 happens-before 關(guān)系
- 3.volatile 寫-讀的內(nèi)存語義
1.volatile 的特性
首先應(yīng)該明確的一點是:當(dāng)聲明共享變量為volatile后舷嗡,對這個變量的讀/寫(分為單元素讀寫,與復(fù)合寫操作)屯曹。不同的讀寫模式下,volatile變量對寫操作的原子性體現(xiàn)是不一樣的往核。
理解volatile特性的一個好辦法是把對volatile變量的單個讀/寫晾腔,看成是同一個鎖對這些單個讀/寫操作做了同步,示例代碼如下所示:
class VolatileFeaturesExample {
volatile long vl = 0L;
public void set(long l){
vl = l;
}
public void getAndIncrement() {
vl++;
}
public long get(){
return vl;
}
}
假設(shè)有多個線程調(diào)用上述程序中的3個方法场绿,這個程序語義和下面的程序語義等價
class VolatileFeaturesExample {
long vl = 0L;
public synchronized void set(long l){
vl = l;
}
public void getAndIncrement() {
long temp = get();
temp +=1L;
set(temp);
}
public syntronized long get(){
return vl;
}
}
解釋
- 如這兩個程序所示剖效,一個volatile 變量的單個讀/寫操作,與一個普通變量的讀/寫操作都是使用同一個鎖來同步焰盗,他們之間的執(zhí)行效果相同璧尸。
- 鎖的happens-before規(guī)則(保證前一個操作結(jié)果對后一個操作立即可見)保證釋放鎖和獲取鎖的兩個線程之間的內(nèi)存可見性。這很好的解釋了對于一個volatile變量的讀熬拒,總能看到任意線程對這個volatile變量最后的寫入爷光。
- 鎖的語義決定了臨界代碼區(qū)的執(zhí)行具有原子性。這意味著澎粟,即使是64位的long型和double型變量蛀序,只要它是volatile變量,對該變量的單個讀/寫就具有原子性活烙,如果是多個volatile操作徐裸,或這是volatile++這種復(fù)合操作,這些操作在整體上是不具有原子性的啸盏。
volatile 自身特性:
1.內(nèi)存可見性:對一個volatile變量的讀重贺,總能看到任意線程對這個volatile變量
最后的寫入(內(nèi)存可見性保證)
2.原子性:對任意單個volatile變量的單獨的讀寫操作,都具有原子性回懦。但對于
volatile++這種復(fù)合操作不具有原子性气笙。
2.volatile 寫-讀建立的 happens-before 關(guān)系
JMM基于共享內(nèi)存模型實現(xiàn)線程之間的通信
從jdk1.5 開始 volatile 變量的寫-讀可以實現(xiàn)線程之間的通信。
volatile & synchronized內(nèi)存語義
從內(nèi)存語義來說怯晕,volatile的寫-讀與鎖的釋放-獲取有相同的內(nèi)存效果:volatile
寫和鎖釋放有相同的內(nèi)存語義潜圃;volatile讀與鎖的獲取有相同的內(nèi)存語義。
下面為volatile變量示例代碼:
class VolatileExample {
int a = 0;//其實是線程共享變量
volatile boolean flag = false;
public void writer(){
a = 1; //1
flag = true; //2
}
public void reader(){
if(flage){ //3
int i = a; //4
......
}
}
}
假設(shè)線程A 執(zhí)行 writer() 方法之后贫贝,線程B 執(zhí)行reader() 方法秉犹。根據(jù)happens-before 規(guī)則,這個過程建立的happens-before規(guī)則可以分為三類:
1.根據(jù)程序次序規(guī)則稚晚,1 happens before 2;3 happens before 4.
2.根據(jù)volatile規(guī)則崇堵,2 happens-before 3
3.根據(jù)happens-before c傳遞性規(guī)則 1 happens-before 4
圖形化形勢如下圖所示:
A線程在寫一個volatile變量后,B線程讀同一個volatile變量客燕。A線程在寫volatile之前所有的可見共享變量鸳劳,在B線程讀到同一個volatile變量之后,將立即變得對B線程可見也搓。
4. volatile 寫-讀的內(nèi)存語義
** volatile 寫內(nèi)存語義 **
當(dāng)寫一個volatile變量時赏廓,JMM會把該線程對應(yīng)的本地內(nèi)存的變量中的變量值強制刷新到主內(nèi)存涵紊。
volatile 讀的內(nèi)存語義
當(dāng)讀一個volatile變量時,JMM會把該線程對應(yīng)的本地內(nèi)存置為無效幔摸。線程接下來將從主內(nèi)存中讀取共享變量摸柄。
volatile內(nèi)存語義 總結(jié)
- 線程A 寫一個volatile 變量,實質(zhì)上是線程A 向接下來讀取這個volatile變量的某個線程發(fā)出了(其對共享變量所做修改的)消息既忆。
- 線程B 讀一個volatile 變量驱负,實質(zhì)上是線程B 接收了之前某個線程發(fā)出的(在寫這個volatile變量之前對共享變量所做修改)消息。
- 線程A 寫一個volatile 變量患雇,隨后線程B讀這個volatile變量跃脊。這個過程實質(zhì)上是線程A通過主內(nèi)存向線程B發(fā)送消息。