關於 volatile 可見性,有序性,記憶體屏障

2021-09-20 06:16:26 字數 2566 閱讀 7758

public class demo1 

system.out.println("test finish");

}public void setflag(boolean flag)

public static void main(string args) ).start();

new thread(()->).start();}}

事實上,控制台不會輸出任何東西,程式能執行到電腦沒電。改為下面這樣則會正常執行。

再看下面這段**,10個執行緒對num執行++操作,直到num=100。volatile 並不能保證結果的正確性,結果有可能大於 100。

public class demo2 

}public static void main(string args) throws interruptedexception ).start();

}thread.sleep(2000);

system.out.println(demo2.num);}}

假如 在num=99 時 thread1 進入 while 條件,由於num++ 不是原子性操作,在執行 num++ 時 cpu 時間片用完而切換到 thread2,此時num 還是99,thread2 進行num++ 後,繼續執行 thread1, num再 +1,結果錯誤。可以拿掉注釋,放大這種情況。

public  void add() throws interruptedexception 

}

所以可以考慮把num++ 變為原子操作優化成以下方式:

//atomicinteger 保證了原子性和可見性

private atomicinteger num = new atomicinteger(0);

public void add() throws interruptedexception

}

這樣就正確了嗎?其實並沒有。因為進入 while 後且在執行 incrementandget() 之前,有可能時間片用完而切換到其他執行緒,也就是說,進入 while 之後的操作不是原子性的操作。可以拿掉1處的注釋來放大這種現象。其實這段**很奇葩,基本上沒人會這麼寫,但是可以通過**學習到很多東西。讓我們繼續優化:

思路就是:怎麼讓程式進入 while之後的操作變為原子操作?第乙個想到的就是 snychronizd,但是就這段**來說,加上snychronizd,就會變成只會有乙個執行緒執行,這並不是本意。當然可以手動釋放 snychronizd 占有的鎖,可以通過 拋異常、break、return,而且 snychronizd 會影響效能,這會顯得很笨重。

那 snychronizd 就排除在外了,有了這個思路,方法就顯而易見了,可以用顯式鎖來保證原子性:

private atomicinteger  num = new atomicinteger(0);

private lock lock = new reentrantlock(false);

public void add() throws interruptedexception

lock.unlock();}}

懶漢式單例問題:

public class singletondoublecheck

public static singletondoublecheck getinstance()

return singleton;}}

這樣寫會造成極大的安全隱患,高併發條件下,如果有兩個執行緒同時執行,都滿足 if 條件,然後對物件進行了兩次初始化,不能保證單例的唯一性。

所以考慮進行同步優化:

public class singletondoublecheck

public static singletondoublecheck getinstance()}}

return singleton;}}

這樣看似已經很完美,其實在實際執行的過程中可能會丟擲 nullpointerexception。因為在實際執行過程中 jvm 為了提高效能 會對指令進行重排序。

簡單說一下重排序:假如在物件物件需要 a、b 兩個單兩個資源,建構函式中需要例項化 a、b 兩個資源和自身的物件,有可能先對自身物件先進行例項化,而此時 a、b 並沒有完成例項化。此時會造成 nullpointerexception。

所以改為:

private volatile static singletondoublecheck singleton;
關於記憶體屏障可以 戳這篇 and 這篇

volatile,可見性,有序性

1.可見性的實現基於volatile的讀取,寫入兩個操作的記憶體語義。2.有序性的實現基於jmm針對編譯器制定的volatile重排序表 能否重排序 第二個操作 第乙個操作 普通讀 寫 volatile讀 volatile寫 普通讀 寫 novolatile讀 nono novolatile寫 no...

volatile 記憶體可見性

public class volatilethread implements runnable catch interruptedexception e flag true system.out.println flag isflag public boolean isflag public cla...

Volatile 記憶體可見性

一 當寫乙個volatile變數時,jmm會把該執行緒對應的本地中的共享變數值重新整理到主記憶體。例子 一 volatile 關鍵字 當多個執行緒進行操作共享資料時,可以保證記憶體中的資料可見。相較於 synchronized 是一種較為輕量級的同步策略。注意 1.volatile 不具備 互斥性 ...