先來看一個例子:
-
public class VolatileTest {
-
-
public static void main(String[] args) {
-
ThreadDemo td = new ThreadDemo();
-
new Thread(td).start();
-
-
while (true) {
-
if (td.isFlag()) {
-
System.out.println("================");
-
break;
-
}
-
}
-
}
-
}
-
-
class ThreadDemo implements Runnable {
-
private boolean flag = false;
-
-
@Override
-
public void run() {
-
try {
-
Thread.sleep(200);
-
} catch (InterruptedException e) {
-
-
}
-
flag = true;
-
System.out.println("falg=" + flag);
-
}
-
-
public boolean isFlag() {
-
return flag;
-
}
-
-
public void setFlag(boolean flag) {
-
this.flag = flag;
-
}
-
}
兩個線程,一個改flag的值,主線程做判斷,結果:
主線程的flag貌似還是false,按理通過Runnable創建的線程訪問的應該是共享數據,那為什么會出現這種情況?這就涉及到內存可見性。
JVM會為每個線程分配一個獨立緩存提高效率。上述例子中我們先在主存中開辟一塊內存,如圖:
那么這個兩個線程,一個是讀(主線程),一個是寫(線程1),我們讓線程1睡了200ms,說明,線程1先執行,每個線程都有一個獨立的緩存,也就是說當線程1需要對主存的共享數值進行改變,它需要先把這個flag復制一份到緩存區中,
然后修改,將來再把這個值寫回主存去,在寫之前,主線程來了,它要讀取現在在內存里面的值,現在是false,當然有一種情況,就是線程1在某個機會將flag=true寫回去,
當時主線程用了while(true),這句話調用了系統底層代碼,效率極高,高到主線程沒有機會再次讀取內存,這就是線程對共享數據操作的不可見。
內存可見性問題:當多個線程操作共享數據時,彼此不可見。
如何解決?同步鎖。
但是用了鎖,代表效率極低,但是我現在我不想加鎖,但是有存在內存可見性的問題,我該怎么辦?
關鍵字volatile:當多個線程進行操作共享操作時,可以保證內存中的數據可見。(內存柵欄,實時刷新)
我們可以認為它是直接在主存操作的,這個實時刷新的操作相比不加,性能略低,但是比加鎖的效率顯然高很多,低在哪?加了這關鍵字,JVM就不能進行指令重排序,無法優化代碼執行。
-
public class VolatileTest {
-
-
public static void main(String[] args) {
-
ThreadDemo td = new ThreadDemo();
-
new Thread(td).start();
-
-
while (true) {
-
if (td.isFlag()) {
-
System.out.println("================");
-
break;
-
}
-
}
-
}
-
}
-
-
class ThreadDemo implements Runnable {
-
private volatile boolean flag = false;
-
-
@Override
-
public void run() {
-
try {
-
Thread.sleep(200);
-
} catch (InterruptedException e) {
-
-
}
-
flag = true;
-
System.out.println("falg=" + flag);
-
}
-
-
public boolean isFlag() {
-
return flag;
-
}
-
-
public void setFlag(boolean flag) {
-
this.flag = flag;
-
}
-
}
volatile相對synchronized是一種輕量級同步策略。但是注意:
- volatile不具備互斥性
- volatile不能保證變量的原子性