學習記錄,若存在錯誤,請指出,謝謝!
首先,介紹一些內存可見性
內存可見性:當多個線程操作共享數據時,彼此不可見
爲什麼會導致這個情況?
在線程運行的過程中,JVM或者說是內存會爲每一個線程分配一個獨立的緩存用於提高效率。
舉個例子:現在有一個讀操作的線程和一個寫操作的線程,對主存中的一個共享變量進行操作
寫操作:線程先從主存中將共享變量讀到線程獨有的緩存中,然後對變量的值進行修改,最後再把修改後的變量的值寫回到主存中。
讀操作:讀取主存中共享變量的值
但是,
讀線程可能在寫線程把修改的變量的值寫回主存之前,就從主存中讀了變量的值,但是這個值是原先的值
public class TestVolatile {
public static void main(String[] args){
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo).start();
while(true){
if(threadDemo.isFlag()){
System.out.println("--------");
break;
}
}
}
}
class ThreadDemo implements Runnable{
private volatile boolean flag = false;
@Override
public void run() {
try {
Thread.sleep(2000);
}catch (InterruptedException e){
}
flag = true;
System.out.println("flag = " + flag);
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
控制檯打印的結果:
這就導致了一個問題,寫線程已經把flag的值修改成了true,但是讀線程(也就是代碼中的main線程)並沒有輸出“-------”。
於是,我做了以下的修改
public class TestVolatile {
public static void main(String[] args){
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo).start();
while(true){
synchronized (threadDemo){
if(threadDemo.isFlag()){
System.out.println("--------");
break;
}
}
}
}
}
在main線程中加上了synchronized關鍵字。於是
但是,使用了synchronize關鍵字之後,會導致程序的效率非常的低下。假設程序中有多個線程,每次訪問synchronized()這行代碼的時候,總是要判斷是否有別的線程佔有鎖,如果有,當前的線程就只能阻塞,直到CPU下次空閒的時候纔可能執行。
爲了保證程序的效率,就不能夠加鎖,但是又要解決內存可見性的問題,就要提到volatile關鍵字了。
volatile關鍵字:當多個線程對共享數據進行操作時,可以保證該共享數據是內存可見的。
原因是:volatile使用了計算機底層的內存柵欄,可以將線程獨立緩存中的值實時更新到主存中去,我們可以理解爲,直接對主存進行操作。
當然,使用了volatile關鍵字也會對系統的性能有一點損耗,因爲每進行一次操作,主存中的內容也會相應改變,但是效率比加鎖還是要高得多的。
private volatile boolean flag = false;
將共享變量flag用volatile關鍵字修飾。
但是,volatile並不能直接替代synchronized關鍵字,因爲相較於synchronized而言,volatile只是一種較爲輕量級的同步策略
注意:
volatile不具備“互斥性”;
volatile不能保證“原子性”;
若要滿足“互斥性”和“原子性”,則不能使用volatile