JAVA併發程式設計:三大特性-可見性、有序性
生活
生活就是生下來,活下去。
————
在JAVA併發程式設計,如果要保證程式的執行緒安全,就要保證程式碼的原子性、可見性、有序性。
昨天聊了原子性。今天來看下可見性、有序性。
什麼是可見性?
當多個執行緒訪問同一個變數時,一個執行緒修改了一個變數的值,其他執行緒可以馬上讀取到修改後的值。
可見性問題
場景說明:
兩個執行緒共享一個變數stop.
A執行緒,當stop為false,則一直執行;為true則停止。
B執行緒,設定stop為true
public class Test { static boolean stop; public static void main(String[] args) throws InterruptedException { new Thread(new Runnable() { @Override public void run() { System.out.println("執行緒A執行中"); while (!stop); System.out.println("執行緒A已停止"); } }).start(); Thread.sleep(10); new Thread(new Runnable() { @Override public void run() { System.out.println("執行緒B執行中"); stop = true; System.out.println("stop設定為true,執行緒B已停止"); } }).start(); } }
其中一次執行結果:
執行緒A執行中
執行緒B執行中
stop設定為true,執行緒B已停止
由此見得,一個普通的變數在多執行緒環境下不具有可見性。
可見性的技術保障
1、Lock
2、synchronized
3、原子類
4、volatile
Lock和synchronized不用說,都是互斥鎖,保障了共享資源在同一時刻只有一個執行緒可以訪問,就不會出現可見性的問題。
volatile保障了一個變數修改以後馬上寫回主記憶體讓其他執行緒可以看到,具體的分析後面再說。
至於原子類為什麼也有可見性,因為他裡面的value本身也是volatile修飾的。
關於volatile的詳細解析,後面開一篇細聊。
什麼是有序性?
即程式執行的順序按照程式碼的先後順序執行。
編譯器和處理器為了提高執行的效率效能,會對執行進行重排序,指令重排序對單執行緒應用沒有影響,但是在多執行緒環境下可能會出現問題。
有序性問題
執行緒A
context = loadContext();
inited = true;
執行緒B
while(!inited ){
sleep
}
doSomethingwithconfig(context);
假設執行緒A發生重排序:
inited = true; context = loadContext();
在這種情況下,B執行緒可能會拿到沒有初始化的變數context。
有序性的技術保障
1、synchronized
2、lock
3、volatile(一定程度的有序性)
synchronized和lock在同一時刻只能讓一個執行緒執行同步方法,當然就是有序的了。
volatile的有序性是指寫入修改後的volatile變數這個操作必定優先於 讀取這個變數。